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