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