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