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