change keys for activating/controlling PiP to color keys instead of number keys
[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), "red")
1159                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1160                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1161         
1162         def available(self):
1163                 return True
1164         
1165         def pipShown(self):
1166                 return self.session.pipshown
1167         
1168         def getShowHideName(self):
1169                 if self.session.pipshown:
1170                         return _("Disable Picture in Picture")
1171                 else:
1172                         return _("Activate Picture in Picture")
1173                 
1174         def getSwapName(self):
1175                 return _("Swap Services")
1176                 
1177         def getMoveName(self):
1178                 return _("Move Picture in Picture")
1179         
1180         def showPiP(self):
1181                 if self.session.pipshown:
1182                         del self.session.pip
1183                         self.session.pipshown = False
1184                 else:
1185                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1186                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1187                         if self.session.pip.playService(newservice):
1188                                 self.session.pipshown = True
1189                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1190                         else:
1191                                 self.session.pipshown = False
1192                                 del self.session.pip
1193                         self.session.nav.playService(newservice)
1194         
1195         def swapPiP(self):
1196                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1197                 if self.session.pip.servicePath:
1198                         servicepath = self.servicelist.getCurrentServicePath()
1199                         ref=servicepath[len(servicepath)-1]
1200                         pipref=self.session.pip.getCurrentService()
1201                         self.session.pip.playService(swapservice)
1202                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1203                         if pipref.toString() != ref.toString(): # is a subservice ?
1204                                 self.session.nav.stopService() # stop portal
1205                                 self.session.nav.playService(pipref) # start subservice
1206                         self.session.pip.servicePath=servicepath
1207         
1208         def movePiP(self):
1209                 self.session.open(PiPSetup, pip = self.session.pip)
1210
1211 from RecordTimer import parseEvent
1212
1213 class InfoBarInstantRecord:
1214         """Instant Record - handles the instantRecord action in order to 
1215         start/stop instant records"""
1216         def __init__(self):
1217                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1218                         {
1219                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1220                         })
1221                 self.recording = []
1222                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1223                 self["BlinkingPoint"].hide()
1224                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1225
1226         def stopCurrentRecording(self, entry = -1):     
1227                 if entry is not None and entry != -1:
1228                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1229                         self.recording.remove(self.recording[entry])
1230
1231         def startInstantRecording(self, limitEvent = False):
1232                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1233                 
1234                 # try to get event info
1235                 event = None
1236                 try:
1237                         service = self.session.nav.getCurrentService()
1238                         epg = eEPGCache.getInstance()
1239                         event = epg.lookupEventTime(serviceref, -1, 0)
1240                         if event is None:
1241                                 info = service.info()
1242                                 ev = info.getEvent(0)
1243                                 event = ev
1244                 except:
1245                         pass
1246
1247                 begin = time.time()
1248                 end = time.time() + 3600 * 10
1249                 name = "instant record"
1250                 description = ""
1251                 eventid = None
1252                 
1253                 if event is not None:
1254                         curEvent = parseEvent(event)
1255                         name = curEvent[2]
1256                         description = curEvent[3]
1257                         eventid = curEvent[4]
1258                         if limitEvent:
1259                                 end = curEvent[1]
1260                 else:
1261                         if limitEvent:
1262                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1263                                 
1264                 data = (begin, end, name, description, eventid)
1265                 
1266                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1267                 recording.dontSave = True
1268                 self.recording.append(recording)
1269                 
1270                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1271                 
1272         def isInstantRecordRunning(self):
1273                 print "self.recording:", self.recording
1274                 if len(self.recording) > 0:
1275                         for x in self.recording:
1276                                 if x.isRunning():
1277                                         return True
1278                 return False
1279
1280         def recordQuestionCallback(self, answer):
1281                 print "pre:\n", self.recording
1282                 
1283                 if answer is None or answer[1] == "no":
1284                         return
1285                 list = []
1286                 recording = self.recording[:]
1287                 for x in recording:
1288                         if not x in self.session.nav.RecordTimer.timer_list:
1289                                 self.recording.remove(x)
1290                         elif x.dontSave and x.isRunning():
1291                                 list.append(TimerEntryComponent(x, False))              
1292
1293                 if answer[1] == "changeduration":
1294                         if len(self.recording) == 1:
1295                                 self.changeDuration(0)
1296                         else:
1297                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1298                 elif answer[1] == "stop":
1299                         if len(self.recording) == 1:
1300                                 self.stopCurrentRecording(0)
1301                         else:
1302                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1303                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1304                         limitEvent = False
1305                         if answer[1] == "event":
1306                                 limitEvent = True
1307                         if answer[1] == "manualduration":
1308                                 self.selectedEntry = len(self.recording)
1309                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1310                         self.startInstantRecording(limitEvent = limitEvent)
1311                         
1312                 print "after:\n", self.recording
1313
1314         def changeDuration(self, entry):
1315                 if entry is not None:
1316                         self.selectedEntry = entry
1317                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1318
1319         def inputCallback(self, value):
1320                 if value is not None:
1321                         print "stopping recording after", int(value), "minutes."
1322                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1323                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1324
1325         def instantRecord(self):
1326                 try:
1327                         stat = os.stat(resolveFilename(SCOPE_HDD))
1328                 except:
1329                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1330                         return
1331
1332                 if self.isInstantRecordRunning():
1333                         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")])
1334                 else:
1335                         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")])
1336
1337 from Tools.ISO639 import LanguageCodes
1338
1339 class InfoBarAudioSelection:
1340         def __init__(self):
1341                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1342                         {
1343                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1344                         })
1345
1346         def audioSelection(self):
1347                 service = self.session.nav.getCurrentService()
1348                 audio = service and service.audioTracks()
1349                 self.audioTracks = audio
1350                 n = audio and audio.getNumberOfTracks() or 0
1351                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1352                 tlist = []
1353                 print "tlist:", tlist
1354                 if n > 0:
1355                         self.audioChannel = service.audioChannel()
1356
1357                         for x in range(n):
1358                                 i = audio.getTrackInfo(x)
1359                                 language = i.getLanguage()
1360                                 description = i.getDescription()
1361         
1362                                 if len(language) == 3:
1363                                         if language in LanguageCodes:
1364                                                 language = LanguageCodes[language][0]
1365         
1366                                 if len(description):
1367                                         description += " (" + language + ")"
1368                                 else:
1369                                         description = language
1370         
1371                                 tlist.append((description, x))
1372                         
1373                         selectedAudio = tlist[0][1]
1374                         tlist.sort(lambda x,y : cmp(x[0], y[0]))
1375
1376                         selection = 2
1377                         for x in tlist:
1378                                 if x[1] != selectedAudio:
1379                                         selection += 1
1380                                 else:
1381                                         break
1382
1383                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1384                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1385                 else:
1386                         del self.audioTracks
1387
1388         def audioSelected(self, audio):
1389                 if audio is not None:
1390                         if isinstance(audio[1], str):
1391                                 if audio[1] == "mode":
1392                                         keys = ["red", "green", "yellow"]
1393                                         selection = self.audioChannel.getCurrentChannel()
1394                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1395                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1396                         else:
1397                                 del self.audioChannel
1398                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1399                                         self.audioTracks.selectTrack(audio[1])
1400                 else:
1401                         del self.audioChannel
1402                 del self.audioTracks
1403
1404         def modeSelected(self, mode):
1405                 if mode is not None:
1406                         self.audioChannel.selectChannel(mode[1])
1407                 del self.audioChannel
1408
1409 class InfoBarSubserviceSelection:
1410         def __init__(self):
1411                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1412                         {
1413                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1414                         })
1415
1416                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1417                         {
1418                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1419                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1420                         }, -1)
1421                 self["SubserviceQuickzapAction"].setEnabled(False)
1422
1423                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1424
1425         def checkSubservicesAvail(self, ev):
1426                 if ev == iPlayableService.evUpdatedEventInfo:
1427                         service = self.session.nav.getCurrentService()
1428                         subservices = service and service.subServices()
1429                         if not subservices or subservices.getNumberOfSubservices() == 0:
1430                                 self["SubserviceQuickzapAction"].setEnabled(False)
1431
1432         def nextSubservice(self):
1433                 self.changeSubservice(+1)
1434
1435         def prevSubservice(self):
1436                 self.changeSubservice(-1)
1437
1438         def changeSubservice(self, direction):
1439                 service = self.session.nav.getCurrentService()
1440                 subservices = service and service.subServices()
1441                 n = subservices and subservices.getNumberOfSubservices()
1442                 if n and n > 0:
1443                         selection = -1
1444                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1445                         for x in range(n):
1446                                 if subservices.getSubservice(x).toString() == ref.toString():
1447                                         selection = x
1448                         if selection != -1:
1449                                 selection += direction
1450                                 if selection >= n:
1451                                         selection=0
1452                                 elif selection < 0:
1453                                         selection=n-1
1454                                 newservice = subservices.getSubservice(selection)
1455                                 if newservice.valid():
1456                                         del subservices
1457                                         del service
1458                                         self.doShow()
1459                                         self.session.nav.playService(newservice)
1460
1461         def subserviceSelection(self):
1462                 service = self.session.nav.getCurrentService()
1463                 subservices = service and service.subServices()
1464                 
1465                 n = subservices and subservices.getNumberOfSubservices()
1466                 selection = 0
1467                 if n and n > 0:
1468                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1469                         tlist = []
1470                         for x in range(n):
1471                                 i = subservices.getSubservice(x)
1472                                 if i.toString() == ref.toString():
1473                                         selection = x
1474                                 tlist.append((i.getName(), i))
1475
1476                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1477
1478                         keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1479
1480                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection + 2, keys = keys)
1481
1482         def subserviceSelected(self, service):
1483                 if not service is None:
1484                         if isinstance(service[1], str):
1485                                 if service[1] == "quickzap":
1486                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1487                                         self.session.open(SubservicesQuickzap, service[2])
1488                         else:
1489                                 self["SubserviceQuickzapAction"].setEnabled(True)
1490                                 self.doShow()
1491                                 self.session.nav.playService(service[1])
1492
1493 class InfoBarAdditionalInfo:
1494         def __init__(self):
1495                 self["NimA"] = Pixmap()
1496                 self["NimB"] = Pixmap()
1497                 self["NimA_Active"] = Pixmap()
1498                 self["NimB_Active"] = Pixmap()
1499
1500                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
1501                 self["TimeshiftPossible"] = self["RecordingPossible"]
1502                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1503
1504                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1505                 res_mgr = eDVBResourceManagerPtr()
1506                 if eDVBResourceManager.getInstance(res_mgr) == 0:
1507                         res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
1508
1509         def tunerUseMaskChanged(self, mask):
1510                 if mask&1:
1511                         self["NimA_Active"].show()
1512                 else:
1513                         self["NimA_Active"].hide()
1514                 if mask&2:
1515                         self["NimB_Active"].show()
1516                 else:
1517                         self["NimB_Active"].hide()
1518
1519         def checkTunerState(self, service):
1520                 info = service.frontendInfo()
1521                 feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
1522                 if feNumber is None:
1523                         self["NimA"].hide()
1524                         self["NimB"].hide()
1525                 elif feNumber == 0:
1526                         self["NimB"].hide()
1527                         self["NimA"].show()
1528                 elif feNumber == 1:
1529                         self["NimA"].hide()
1530                         self["NimB"].show()
1531
1532         def gotServiceEvent(self, ev):
1533                 service = self.session.nav.getCurrentService()
1534                 if ev == iPlayableService.evStart:
1535                         self.checkTunerState(service)
1536
1537 class InfoBarNotifications:
1538         def __init__(self):
1539                 self.onExecBegin.append(self.checkNotifications)
1540                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1541                 self.onClose.append(self.__removeNotification)
1542         
1543         def __removeNotification(self):
1544                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1545         
1546         def checkNotificationsIfExecing(self):
1547                 if self.execing:
1548                         self.checkNotifications()
1549
1550         def checkNotifications(self):
1551                 if len(Notifications.notifications):
1552                         n = Notifications.notifications[0]
1553                         Notifications.notifications = Notifications.notifications[1:]
1554                         cb = n[0]
1555                         if cb is not None:
1556                                 self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1557                         else:
1558                                 self.session.open(n[1], *n[2], **n[3])
1559
1560 class InfoBarServiceNotifications:
1561         def __init__(self):
1562                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1563                         {
1564                                 iPlayableService.evEnd: self.serviceHasEnded
1565                         })
1566
1567         def serviceHasEnded(self):
1568                 print "service end!"
1569
1570                 try:
1571                         self.setSeekState(self.SEEK_STATE_PLAY)
1572                 except:
1573                         pass
1574
1575 class InfoBarCueSheetSupport:
1576         CUT_TYPE_IN = 0
1577         CUT_TYPE_OUT = 1
1578         CUT_TYPE_MARK = 2
1579         CUT_TYPE_LAST = 3
1580         
1581         ENABLE_RESUME_SUPPORT = False
1582         
1583         def __init__(self):
1584                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1585                         {
1586                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1587                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1588                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1589                         }, prio=1) 
1590                 
1591                 self.cut_list = [ ]
1592                 self.is_closing = False
1593                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1594                         {
1595                                 iPlayableService.evStart: self.__serviceStarted,
1596                         })
1597
1598         def __serviceStarted(self):
1599                 if self.is_closing:
1600                         return
1601                 print "new service started! trying to download cuts!"
1602                 self.downloadCuesheet()
1603                 
1604                 if self.ENABLE_RESUME_SUPPORT:
1605                         last = None
1606                         
1607                         for (pts, what) in self.cut_list:
1608                                 if what == self.CUT_TYPE_LAST:
1609                                         last = pts
1610                         
1611                         if last is not None:
1612                                 self.resume_point = last
1613                                 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1614
1615         def playLastCB(self, answer):
1616                 if answer == True:
1617                         seekable = self.__getSeekable()
1618                         if seekable is not None:
1619                                 seekable.seekTo(self.resume_point)
1620
1621         def __getSeekable(self):
1622                 service = self.session.nav.getCurrentService()
1623                 if service is None:
1624                         return None
1625                 return service.seek()
1626
1627         def cueGetCurrentPosition(self):
1628                 seek = self.__getSeekable()
1629                 if seek is None:
1630                         return None
1631                 r = seek.getPlayPosition()
1632                 if r[0]:
1633                         return None
1634                 return long(r[1])
1635
1636         def jumpPreviousNextMark(self, cmp, alternative=None):
1637                 current_pos = self.cueGetCurrentPosition()
1638                 if current_pos is None:
1639                         return
1640                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1641                 if mark is not None:
1642                         pts = mark[0]
1643                 elif alternative is not None:
1644                         pts = alternative
1645                 else:
1646                         return
1647
1648                 seekable = self.__getSeekable()
1649                 if seekable is not None:
1650                         seekable.seekTo(pts)
1651
1652         def jumpPreviousMark(self):
1653                 # we add 2 seconds, so if the play position is <2s after
1654                 # the mark, the mark before will be used
1655                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1656
1657         def jumpNextMark(self):
1658                 self.jumpPreviousNextMark(lambda x: x)
1659
1660         def getNearestCutPoint(self, pts, cmp=abs):
1661                 # can be optimized
1662                 nearest = None
1663                 for cp in self.cut_list:
1664                         diff = cmp(cp[0] - pts)
1665                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1666                                 nearest = cp
1667                 return nearest
1668
1669         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1670                 current_pos = self.cueGetCurrentPosition()
1671                 if current_pos is None:
1672                         print "not seekable"
1673                         return
1674                 
1675                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1676                 
1677                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1678                         if onlyreturn:
1679                                 return nearest_cutpoint
1680                         if not onlyadd:
1681                                 self.removeMark(nearest_cutpoint)
1682                 elif not onlyremove and not onlyreturn:
1683                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1684                 
1685                 if onlyreturn:
1686                         return None
1687
1688         def addMark(self, point):
1689                 bisect.insort(self.cut_list, point)
1690                 self.uploadCuesheet()
1691
1692         def removeMark(self, point):
1693                 self.cut_list.remove(point)
1694                 self.uploadCuesheet()
1695
1696         def __getCuesheet(self):
1697                 service = self.session.nav.getCurrentService()
1698                 if service is None:
1699                         return None
1700                 return service.cueSheet()
1701
1702         def uploadCuesheet(self):
1703                 cue = self.__getCuesheet()
1704
1705                 if cue is None:
1706                         print "upload failed, no cuesheet interface"
1707                         return
1708                 cue.setCutList(self.cut_list)
1709
1710         def downloadCuesheet(self):
1711                 cue = self.__getCuesheet()
1712
1713                 if cue is None:
1714                         print "upload failed, no cuesheet interface"
1715                         return
1716                 self.cut_list = cue.getCutList()
1717
1718 class InfoBarSummary(Screen):
1719         skin = """
1720         <screen position="0,0" size="132,64">
1721                 <widget source="CurrentTime" render="Label" position="56,46" size="82,18" font="Regular;16" >
1722                         <convert type="ClockToText">WithSeconds</convert>
1723                 </widget>
1724                 <widget source="CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
1725                         <convert type="ServiceName">Name</convert>
1726                 </widget>
1727         </screen>"""
1728
1729         def __init__(self, session, parent):
1730                 Screen.__init__(self, session)
1731                 self["CurrentService"] = CurrentService(self.session.nav)
1732                 self["CurrentTime"] = Clock()
1733
1734 class InfoBarSummarySupport:
1735         def __init__(self):
1736                 pass
1737         
1738         def createSummary(self):
1739                 return InfoBarSummary
1740
1741 class InfoBarTeletextPlugin:
1742         def __init__(self):
1743                 self.teletext_plugin = None
1744                 
1745                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1746                         self.teletext_plugin = p
1747                 
1748                 if self.teletext_plugin is not None:
1749                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1750                                 {
1751                                         "startTeletext": (self.startTeletext, _("View teletext..."))
1752                                 })
1753                 else:
1754                         print "no teletext plugin found!"
1755
1756         def startTeletext(self):
1757                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
1758
1759 class InfoBarSubtitleSupport(object):
1760         def __init__(self):
1761                 object.__init__(self)
1762                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
1763                 self.__subtitles_enabled = False
1764
1765                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1766                         {
1767                                 iPlayableService.evStart: self.__serviceStarted,
1768                         })
1769
1770         def __serviceStarted(self):
1771                 # reenable if it was enabled
1772                 r = self.__subtitles_enabled
1773                 self.__subtitles_enabled = False
1774                 self.__selected_subtitle = None
1775                 self.setSubtitlesEnable(r)
1776
1777         def getCurrentServiceSubtitle(self):
1778                 service = self.session.nav.getCurrentService()
1779                 return service and service.subtitle()
1780         
1781         def setSubtitlesEnable(self, enable=True):
1782                 subtitle = self.getCurrentServiceSubtitle()
1783                 if enable and self.__selected_subtitle:
1784                         if subtitle and not self.__subtitles_enabled:
1785                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1786                                 self.subtitle_window.show()
1787                                 self.__subtitles_enabled = True
1788                 else:
1789                         if subtitle:
1790                                 subtitle.disableSubtitles(self.subtitle_window.instance)
1791
1792                         self.subtitle_window.hide()
1793                         self.__subtitles_enabled = False
1794
1795         def setSelectedSubtitle(self, subtitle):
1796                 if self.__selected_subtitle != subtitle and self.subtitles_enabled:
1797                         # kick
1798                         self.__selected_subtitle = subtitle
1799                         self.__serviceStarted()
1800                 else:
1801                         self.__selected_subtitle = subtitle
1802
1803         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
1804         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)