d343f83327ba6ef3fcc65437d48f8700f7241d86
[enigma2.git] / lib / python / Screens / InfoBarGenerics.py
1 from ChannelSelection import ChannelSelection, BouquetSelector
2
3 from Components.ActionMap import ActionMap, HelpableActionMap
4 from Components.ActionMap import NumberActionMap
5 from Components.BlinkingPixmap import BlinkingPixmapConditional
6 from Components.Harddisk import harddiskmanager
7 from Components.Input import Input
8 from Components.Label import *
9 from Components.Pixmap import Pixmap, PixmapConditional
10 from Components.PluginComponent import plugins
11 from Components.ProgressBar import *
12 from Components.ServiceEventTracker import ServiceEventTracker
13 from Components.Sources.CurrentService import CurrentService
14 from Components.Sources.EventInfo import EventInfo
15 from Components.Sources.FrontendStatus import FrontendStatus
16 from Components.Sources.Boolean import Boolean
17 from Components.Sources.Clock import Clock
18 from Components.TimerList import TimerEntryComponent
19 from Components.config import config, ConfigBoolean
20
21 from EpgSelection import EPGSelection
22 from Plugins.Plugin import PluginDescriptor
23
24 from Screen import Screen
25 from Screens.ChoiceBox import ChoiceBox
26 from Screens.Dish import Dish
27 from Screens.EventView import EventViewEPGSelect, EventViewSimple
28 from Screens.InputBox import InputBox
29 from Screens.MessageBox import MessageBox
30 from Screens.MinuteInput import MinuteInput
31 from Screens.TimerSelection import TimerSelection
32 from Screens.PictureInPicture import PictureInPicture
33 from Screens.SubtitleDisplay import SubtitleDisplay
34 from 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"] = HelpableActionMap(self, "InfobarRdsActions",
572                 {
573                         "startRassInteractive": (self.startRassInteractive, _("View Rass interactive..."))
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                                         return 1
656                                 else:
657                                         return HelpableActionMap.action(self, contexts, action)
658
659                 self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions", 
660                         {
661                                 "playpauseService": (self.playpauseService, _("pause")),
662                                 "pauseService": (self.pauseService, _("pause")),
663                                 "unPauseService": (self.unPauseService, _("continue")),
664                                 
665                                 "seekFwd": (self.seekFwd, _("skip forward")),
666                                 "seekFwdDown": self.seekFwdDown,
667                                 "seekFwdUp": self.seekFwdUp,
668                                 "seekBack": (self.seekBack, _("skip backward")),
669                                 "seekBackDown": self.seekBackDown,
670                                 "seekBackUp": self.seekBackUp,
671                         }, prio=-1)
672                         # give them a little more priority to win over color buttons
673
674                 self["SeekActions"].setEnabled(False)
675
676                 self.seekstate = self.SEEK_STATE_PLAY
677                 self.onClose.append(self.delTimer)
678                 
679                 self.fwdtimer = False
680                 self.fwdKeyTimer = eTimer()
681                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
682
683                 self.rwdtimer = False
684                 self.rwdKeyTimer = eTimer()
685                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
686                 
687                 self.onPlayStateChanged = [ ]
688                 
689                 self.lockedBecauseOfSkipping = False
690
691         def up(self):
692                 pass
693         
694         def down(self):
695                 pass
696         
697         def delTimer(self):
698                 del self.fwdKeyTimer
699                 del self.rwdKeyTimer
700         
701         def getSeek(self):
702                 service = self.session.nav.getCurrentService()
703                 if service is None:
704                         return None
705
706                 seek = service.seek()
707
708                 if seek is None or not seek.isCurrentlySeekable():
709                         return None
710                 
711                 return seek
712         
713         def isSeekable(self):
714                 if self.getSeek() is None:
715                         return False
716                 return True
717
718         def __seekableStatusChanged(self):
719                 print "seekable status changed!"
720                 if not self.isSeekable():
721                         self["SeekActions"].setEnabled(False)
722                         print "not seekable, return to play"
723                         self.setSeekState(self.SEEK_STATE_PLAY)
724                 else:
725                         self["SeekActions"].setEnabled(True)
726                         print "seekable"
727
728         def __serviceStarted(self):
729                 self.seekstate = self.SEEK_STATE_PLAY
730                 self.__seekableStatusChanged()
731
732         def setSeekState(self, state):
733                 service = self.session.nav.getCurrentService()
734                 
735                 if service is None:
736                         return False
737                 
738                 if not self.isSeekable():
739                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
740                                 state = self.SEEK_STATE_PLAY
741
742                 pauseable = service.pause()
743
744                 if pauseable is None:
745                         print "not pauseable."
746                         state = self.SEEK_STATE_PLAY
747
748                 oldstate = self.seekstate
749                 self.seekstate = state
750
751                 for i in range(3):
752                         if oldstate[i] != self.seekstate[i]:
753                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
754
755                 for c in self.onPlayStateChanged:
756                         c(self.seekstate)
757
758                 self.checkSkipShowHideLock()
759
760                 return True
761         
762         def playpauseService(self):
763                 if self.seekstate != self.SEEK_STATE_PLAY:
764                         self.unPauseService()
765                 else:
766                         self.pauseService()
767
768         def pauseService(self):
769                 if self.seekstate == self.SEEK_STATE_PAUSE:
770                         print "pause, but in fact unpause"
771                         self.unPauseService()
772                 else:
773                         if self.seekstate == self.SEEK_STATE_PLAY:
774                                 print "yes, playing."
775                         else:
776                                 print "no", self.seekstate
777                         print "pause"
778                         self.setSeekState(self.SEEK_STATE_PAUSE);
779                 
780         def unPauseService(self):
781                 print "unpause"
782                 if self.seekstate == self.SEEK_STATE_PLAY:
783                         return 0
784                 self.setSeekState(self.SEEK_STATE_PLAY)
785         
786         def doSeek(self, seektime):
787                 print "doseek", seektime
788                 service = self.session.nav.getCurrentService()
789                 if service is None:
790                         return
791                 
792                 seekable = self.getSeek()
793                 if seekable is None:
794                         return
795                 
796                 seekable.seekTo(90 * seektime)
797
798         def seekFwdDown(self):
799                 print "start fwd timer"
800                 self.fwdtimer = True
801                 self.fwdKeyTimer.start(1000)
802
803         def seekBackDown(self):
804                 print "start rewind timer"
805                 self.rwdtimer = True
806                 self.rwdKeyTimer.start(1000)
807
808         def seekFwdUp(self):
809                 print "seekFwdUp"
810                 if self.fwdtimer:
811                         self.fwdKeyTimer.stop()
812                         self.fwdtimer = False
813                         self.seekFwd()
814
815         def seekFwd(self):
816                 lookup = {
817                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
818                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
819                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
820                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
821                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
822                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
823                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
824                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
825                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
826                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
827                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
828                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
829                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
830                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
831                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER,
832                                 self.SEEK_STATE_EOF: self.SEEK_STATE_EOF,
833                         }
834                 self.setSeekState(lookup[self.seekstate])
835         
836         def seekBackUp(self):
837                 print "seekBackUp"
838                 if self.rwdtimer:
839                         self.rwdKeyTimer.stop()
840                         self.rwdtimer = False
841                         self.seekBack()
842                 
843         def seekBack(self):
844                 lookup = {
845                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
846                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
847                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
848                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
849                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
850                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
851                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
852                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
853                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
854                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
855                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
856                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
857                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
858                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
859                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE,
860                                 self.SEEK_STATE_EOF: self.SEEK_STATE_BACK_16X,
861                         }
862                 self.setSeekState(lookup[self.seekstate])
863                 
864                 if self.seekstate == self.SEEK_STATE_PAUSE:
865                         seekable = self.getSeek()
866                         if seekable is not None:
867                                 seekable.seekRelative(-1, 3)
868
869         def fwdTimerFire(self):
870                 print "Display seek fwd"
871                 self.fwdKeyTimer.stop()
872                 self.fwdtimer = False
873                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
874                 
875         def fwdSeekTo(self, minutes):
876                 print "Seek", minutes, "minutes forward"
877                 if minutes != 0:
878                         seekable = self.getSeek()
879                         if seekable is not None:
880                                 seekable.seekRelative(1, minutes * 60 * 90000)
881         
882         def rwdTimerFire(self):
883                 print "rwdTimerFire"
884                 self.rwdKeyTimer.stop()
885                 self.rwdtimer = False
886                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
887         
888         def rwdSeekTo(self, minutes):
889                 print "rwdSeekTo"
890                 self.fwdSeekTo(0 - minutes)
891         
892         def checkSkipShowHideLock(self):
893                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
894                 
895                 if config.usage.show_infobar_on_zap.value:
896                         if self.lockedBecauseOfSkipping and not wantlock:
897                                 self.unlockShow()
898                                 self.lockedBecauseOfSkipping = False
899                 
900                         if wantlock and not self.lockedBecauseOfSkipping:
901                                 self.lockShow()
902                                 self.lockedBecauseOfSkipping = True
903
904         def __evEOF(self):
905                 if self.seekstate == self.SEEK_STATE_EOF:
906                         return
907                 if self.seekstate[1] < 0: # SEEK_STATE_BACK_*X
908                         print "end of stream while seeking back, ignoring."
909                         return
910
911                 # if we are seeking, we try to end up ~1s before the end, and pause there.
912                 if not self.seekstate in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
913                         self.setSeekState(self.SEEK_STATE_EOF)
914                         self.seekRelativeToEnd(-90000)
915                 else:
916                         self.setSeekState(self.SEEK_STATE_EOF)
917         
918         def __evSOF(self):
919                 self.setSeekState(self.SEEK_STATE_PLAY)
920                 self.doSeek(0)
921
922         def seekRelative(self, diff):
923                 seekable = self.getSeek()
924                 if seekable is not None:
925                         print "seekRelative: res:", seekable.seekRelative(1, diff)
926                 else:
927                         print "seek failed!"
928
929         def seekRelativeToEnd(self, diff):
930                 assert diff <= 0, "diff is expected to be negative!"
931
932                 # might sound like an evil hack, but:
933                 # if we seekRelativeToEnd(0), we expect to be at the end, which is what we want,
934                 # and we don't get that by passing 0 here (it would seek to begin).
935                 if diff == 0:
936                         diff = -1
937
938                 # relative-to-end seeking is implemented as absolutes seeks with negative time
939                 self.seekAbsolute(diff)
940
941         def seekAbsolute(self, abs):
942                 seekable = self.getSeek()
943                 if seekable is not None:
944                         seekable.seekTo(abs)
945
946 from Screens.PVRState import PVRState, TimeshiftState
947
948 class InfoBarPVRState:
949         def __init__(self, screen=PVRState):
950                 self.onPlayStateChanged.append(self.__playStateChanged)
951                 self.pvrStateDialog = self.session.instantiateDialog(screen)
952                 self.onShow.append(self._mayShow)
953                 self.onHide.append(self.pvrStateDialog.hide)
954
955         def _mayShow(self):
956                 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
957                         self.pvrStateDialog.show()
958
959         def __playStateChanged(self, state):
960                 playstateString = state[3]
961                 self.pvrStateDialog["state"].setText(playstateString)
962                 self._mayShow()
963
964 class InfoBarTimeshiftState(InfoBarPVRState):
965         def __init__(self):
966                 InfoBarPVRState.__init__(self, screen=TimeshiftState)
967
968         def _mayShow(self):
969                 if self.execing and self.timeshift_enabled:
970                         self.pvrStateDialog.show()
971
972 class InfoBarShowMovies:
973
974         # i don't really like this class. 
975         # it calls a not further specified "movie list" on up/down/movieList,
976         # so this is not more than an action map
977         def __init__(self):
978                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
979                         {
980                                 "movieList": (self.showMovies, "movie list"),
981                                 "up": (self.showMovies, "movie list"),
982                                 "down": (self.showMovies, "movie list")
983                         })
984
985 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
986
987 # Hrmf.
988 #
989 # Timeshift works the following way:
990 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
991 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
992 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
993 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
994 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
995 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
996 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
997 #
998
999 # in other words:
1000 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
1001 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
1002 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
1003 # - the user can now PVR around
1004 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
1005 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
1006 # after!
1007 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
1008 # - if the user rewinds, or press pause, timeshift will be activated again
1009
1010 # note that a timeshift can be enabled ("recording") and
1011 # activated (currently time-shifting).
1012
1013 class InfoBarTimeshift:
1014         def __init__(self):
1015                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
1016                         {
1017                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
1018                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
1019                         }, prio=1)
1020                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1021                         {
1022                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1023                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1024                         }, prio=-1) # priority over record
1025
1026                 self.timeshift_enabled = 0
1027                 self.timeshift_state = 0
1028                 self.ts_rewind_timer = eTimer()
1029                 self.ts_rewind_timer.timeout.get().append(self.rewindService)
1030
1031                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1032                         {
1033                                 iPlayableService.evStart: self.__serviceStarted,
1034                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1035                         })
1036
1037         def getTimeshift(self):
1038                 service = self.session.nav.getCurrentService()
1039                 return service and service.timeshift()
1040
1041         def startTimeshift(self):
1042                 print "enable timeshift"
1043                 ts = self.getTimeshift()
1044                 if ts is None:
1045                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1046                         print "no ts interface"
1047                         return 0
1048
1049                 if self.timeshift_enabled:
1050                         print "hu, timeshift already enabled?"
1051                 else:
1052                         if not ts.startTimeshift():
1053                                 self.timeshift_enabled = 1
1054
1055                                 # we remove the "relative time" for now.
1056                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1057
1058                                 # PAUSE.
1059                                 self.setSeekState(self.SEEK_STATE_PAUSE)
1060
1061                                 # enable the "TimeshiftEnableActions", which will override
1062                                 # the startTimeshift actions
1063                                 self.__seekableStatusChanged()
1064                         else:
1065                                 print "timeshift failed"
1066
1067         def stopTimeshift(self):
1068                 if not self.timeshift_enabled:
1069                         return 0
1070                 print "disable timeshift"
1071                 ts = self.getTimeshift()
1072                 if ts is None:
1073                         return 0
1074                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1075
1076         def stopTimeshiftConfirmed(self, confirmed):
1077                 if not confirmed:
1078                         return
1079
1080                 ts = self.getTimeshift()
1081                 if ts is None:
1082                         return
1083
1084                 ts.stopTimeshift()
1085                 self.timeshift_enabled = 0
1086
1087                 # disable actions
1088                 self.__seekableStatusChanged()
1089
1090         # activates timeshift, and seeks to (almost) the end
1091         def activateTimeshiftEnd(self, back = True):
1092                 ts = self.getTimeshift()
1093                 print "activateTimeshiftEnd"
1094
1095                 if ts is None:
1096                         return
1097
1098                 if ts.isTimeshiftActive():
1099                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1100                         self.pauseService()
1101                 else:
1102                         print "play, ..."
1103                         ts.activateTimeshift() # activate timeshift will automatically pause
1104                         self.setSeekState(self.SEEK_STATE_PAUSE)
1105                         self.seekRelativeToEnd(-90000) # seek approx. 1 sec before end
1106
1107                 if back:
1108                         self.ts_rewind_timer.start(200, 1)
1109
1110         def rewindService(self):
1111                 self.setSeekState(self.SEEK_STATE_BACK_16X)
1112
1113         # same as activateTimeshiftEnd, but pauses afterwards.
1114         def activateTimeshiftEndAndPause(self):
1115                 print "activateTimeshiftEndAndPause"
1116                 #state = self.seekstate
1117                 self.activateTimeshiftEnd(False)
1118         
1119         def __seekableStatusChanged(self):
1120                 enabled = False
1121                 
1122                 print "self.isSeekable", self.isSeekable()
1123                 print "self.timeshift_enabled", self.timeshift_enabled
1124                 
1125                 # when this service is not seekable, but timeshift
1126                 # is enabled, this means we can activate
1127                 # the timeshift
1128                 if not self.isSeekable() and self.timeshift_enabled:
1129                         enabled = True
1130
1131                 print "timeshift activate:", enabled
1132                 self["TimeshiftActivateActions"].setEnabled(enabled)
1133
1134         def __serviceStarted(self):
1135                 self.timeshift_enabled = False
1136                 self.__seekableStatusChanged()
1137
1138 from Screens.PiPSetup import PiPSetup
1139
1140 class InfoBarExtensions:
1141         EXTENSION_SINGLE = 0
1142         EXTENSION_LIST = 1
1143         
1144         def __init__(self):
1145                 self.list = []
1146                 
1147                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1148                         {
1149                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1150                         })
1151
1152         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1153                 self.list.append((type, extension, key))
1154                 
1155         def updateExtension(self, extension, key = None):
1156                 self.extensionsList.append(extension)
1157                 if key is not None:
1158                         if self.extensionKeys.has_key(key):
1159                                 key = None
1160                 
1161                 if key is None:
1162                         for x in self.availableKeys:
1163                                 if not self.extensionKeys.has_key(x):
1164                                         key = x
1165                                         break
1166
1167                 if key is not None:
1168                         self.extensionKeys[key] = len(self.extensionsList) - 1
1169                         
1170         def updateExtensions(self):
1171                 self.extensionsList = []
1172                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1173                 self.extensionKeys = {}
1174                 for x in self.list:
1175                         if x[0] == self.EXTENSION_SINGLE:
1176                                 self.updateExtension(x[1], x[2])
1177                         else:
1178                                 for y in x[1]():
1179                                         self.updateExtension(y[0], y[1])
1180
1181
1182         def showExtensionSelection(self):
1183                 self.updateExtensions()
1184                 extensionsList = self.extensionsList[:]
1185                 keys = []
1186                 list = []
1187                 for x in self.availableKeys:
1188                         if self.extensionKeys.has_key(x):
1189                                 entry = self.extensionKeys[x]
1190                                 extension = self.extensionsList[entry]
1191                                 if extension[2]():
1192                                         name = str(extension[0]())
1193                                         list.append((extension[0](), extension))
1194                                         keys.append(x)
1195                                         extensionsList.remove(extension)
1196                                 else:
1197                                         extensionsList.remove(extension)
1198                 for x in extensionsList:
1199                         list.append((x[0](), x))
1200                 keys += [""] * len(extensionsList)
1201                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys)
1202
1203         def extensionCallback(self, answer):
1204                 if answer is not None:
1205                         answer[1][1]()
1206
1207 from Tools.BoundFunction import boundFunction
1208
1209 # depends on InfoBarExtensions
1210 from Components.PluginComponent import plugins
1211
1212 class InfoBarPlugins:
1213         def __init__(self):
1214                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1215                 
1216         def getPluginName(self, name):
1217                 return name
1218                 
1219         def getPluginList(self):
1220                 list = []
1221                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1222                         list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None))
1223                 return list
1224
1225         def runPlugin(self, plugin):
1226                 plugin(session = self.session)
1227
1228 # depends on InfoBarExtensions
1229 class InfoBarSleepTimer:
1230         def __init__(self):
1231                 self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, self.available), "1")      
1232                 
1233         def available(self):
1234                 return True
1235
1236         def getSleepTimerName(self):
1237                 return _("Sleep Timer")
1238
1239         def showSleepTimerSetup(self):
1240                 self.session.open(SleepTimerEdit)
1241
1242 # depends on InfoBarExtensions
1243 class InfoBarPiP:
1244         def __init__(self):
1245                 self.session.pipshown = False
1246
1247                 self.addExtension((self.getShowHideName, self.showPiP, self.available), "blue")
1248                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1249                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1250         
1251         def available(self):
1252                 return True
1253         
1254         def pipShown(self):
1255                 return self.session.pipshown
1256         
1257         def getShowHideName(self):
1258                 if self.session.pipshown:
1259                         return _("Disable Picture in Picture")
1260                 else:
1261                         return _("Activate Picture in Picture")
1262                 
1263         def getSwapName(self):
1264                 return _("Swap Services")
1265                 
1266         def getMoveName(self):
1267                 return _("Move Picture in Picture")
1268         
1269         def showPiP(self):
1270                 if self.session.pipshown:
1271                         del self.session.pip
1272                         self.session.pipshown = False
1273                 else:
1274                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1275                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1276                         if self.session.pip.playService(newservice):
1277                                 self.session.pipshown = True
1278                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1279                         else:
1280                                 self.session.pipshown = False
1281                                 del self.session.pip
1282                         self.session.nav.playService(newservice)
1283         
1284         def swapPiP(self):
1285                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1286                 if self.session.pip.servicePath:
1287                         servicepath = self.servicelist.getCurrentServicePath()
1288                         ref=servicepath[len(servicepath)-1]
1289                         pipref=self.session.pip.getCurrentService()
1290                         self.session.pip.playService(swapservice)
1291                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1292                         if pipref.toString() != ref.toString(): # is a subservice ?
1293                                 self.session.nav.stopService() # stop portal
1294                                 self.session.nav.playService(pipref) # start subservice
1295                         self.session.pip.servicePath=servicepath
1296         
1297         def movePiP(self):
1298                 self.session.open(PiPSetup, pip = self.session.pip)
1299
1300 from RecordTimer import parseEvent
1301
1302 class InfoBarInstantRecord:
1303         """Instant Record - handles the instantRecord action in order to 
1304         start/stop instant records"""
1305         def __init__(self):
1306                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1307                         {
1308                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1309                         })
1310                 self.recording = []
1311                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1312                 self["BlinkingPoint"].hide()
1313                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1314
1315         def stopCurrentRecording(self, entry = -1):     
1316                 if entry is not None and entry != -1:
1317                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1318                         self.recording.remove(self.recording[entry])
1319
1320         def startInstantRecording(self, limitEvent = False):
1321                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1322                 
1323                 # try to get event info
1324                 event = None
1325                 try:
1326                         service = self.session.nav.getCurrentService()
1327                         epg = eEPGCache.getInstance()
1328                         event = epg.lookupEventTime(serviceref, -1, 0)
1329                         if event is None:
1330                                 info = service.info()
1331                                 ev = info.getEvent(0)
1332                                 event = ev
1333                 except:
1334                         pass
1335
1336                 begin = time()
1337                 end = time() + 3600 * 10
1338                 name = "instant record"
1339                 description = ""
1340                 eventid = None
1341                 
1342                 if event is not None:
1343                         curEvent = parseEvent(event)
1344                         name = curEvent[2]
1345                         description = curEvent[3]
1346                         eventid = curEvent[4]
1347                         if limitEvent:
1348                                 end = curEvent[1]
1349                 else:
1350                         if limitEvent:
1351                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1352                                 
1353                 data = (begin, end, name, description, eventid)
1354                 
1355                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1356                 recording.dontSave = True
1357                 self.recording.append(recording)
1358                 
1359                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1360                 
1361         def isInstantRecordRunning(self):
1362                 print "self.recording:", self.recording
1363                 if len(self.recording) > 0:
1364                         for x in self.recording:
1365                                 if x.isRunning():
1366                                         return True
1367                 return False
1368
1369         def recordQuestionCallback(self, answer):
1370                 print "pre:\n", self.recording
1371                 
1372                 if answer is None or answer[1] == "no":
1373                         return
1374                 list = []
1375                 recording = self.recording[:]
1376                 for x in recording:
1377                         if not x in self.session.nav.RecordTimer.timer_list:
1378                                 self.recording.remove(x)
1379                         elif x.dontSave and x.isRunning():
1380                                 list.append(TimerEntryComponent(x, False))              
1381
1382                 if answer[1] == "changeduration":
1383                         if len(self.recording) == 1:
1384                                 self.changeDuration(0)
1385                         else:
1386                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1387                 elif answer[1] == "stop":
1388                         if len(self.recording) == 1:
1389                                 self.stopCurrentRecording(0)
1390                         else:
1391                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1392                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1393                         limitEvent = False
1394                         if answer[1] == "event":
1395                                 limitEvent = True
1396                         if answer[1] == "manualduration":
1397                                 self.selectedEntry = len(self.recording)
1398                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1399                         self.startInstantRecording(limitEvent = limitEvent)
1400                         
1401                 print "after:\n", self.recording
1402
1403         def changeDuration(self, entry):
1404                 if entry is not None:
1405                         self.selectedEntry = entry
1406                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1407
1408         def inputCallback(self, value):
1409                 if value is not None:
1410                         print "stopping recording after", int(value), "minutes."
1411                         self.recording[self.selectedEntry].end = time() + 60 * int(value)
1412                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1413
1414         def instantRecord(self):
1415                 try:
1416                         stat = os_stat(resolveFilename(SCOPE_HDD))
1417                 except:
1418                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1419                         return
1420
1421                 if self.isInstantRecordRunning():
1422                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1423                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1424                                 list=[(_("stop recording"), "stop"), \
1425                                 (_("change recording (duration)"), "changeduration"), \
1426                                 (_("add recording (indefinitely)"), "indefinitely"), \
1427                                 (_("add recording (stop after current event)"), "event"), \
1428                                 (_("add recording (enter recording duration)"), "manualduration"), \
1429                                 (_("do nothing"), "no")])
1430                 else:
1431                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1432                                 title=_("Start recording?"), \
1433                                 list=[(_("add recording (indefinitely)"), "indefinitely"), \
1434                                 (_("add recording (stop after current event)"), "event"), \
1435                                 (_("add recording (enter recording duration)"), "manualduration"), \
1436                                 (_("don't record"), "no")])
1437
1438 from Tools.ISO639 import LanguageCodes
1439
1440 class InfoBarAudioSelection:
1441         def __init__(self):
1442                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1443                         {
1444                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1445                         })
1446
1447         def audioSelection(self):
1448                 service = self.session.nav.getCurrentService()
1449                 audio = service and service.audioTracks()
1450                 self.audioTracks = audio
1451                 n = audio and audio.getNumberOfTracks() or 0
1452                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1453                 tlist = []
1454                 print "tlist:", tlist
1455                 if n > 0:
1456                         self.audioChannel = service.audioChannel()
1457
1458                         for x in range(n):
1459                                 i = audio.getTrackInfo(x)
1460                                 language = i.getLanguage()
1461                                 description = i.getDescription()
1462         
1463                                 if LanguageCodes.has_key(language):
1464                                         language = LanguageCodes[language][0]
1465         
1466                                 if len(description):
1467                                         description += " (" + language + ")"
1468                                 else:
1469                                         description = language
1470         
1471                                 tlist.append((description, x))
1472                         
1473                         selectedAudio = tlist[0][1]
1474                         tlist.sort(lambda x,y : cmp(x[0], y[0]))
1475
1476                         selection = 2
1477                         for x in tlist:
1478                                 if x[1] != selectedAudio:
1479                                         selection += 1
1480                                 else:
1481                                         break
1482
1483                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1484                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1485                 else:
1486                         del self.audioTracks
1487
1488         def audioSelected(self, audio):
1489                 if audio is not None:
1490                         if isinstance(audio[1], str):
1491                                 if audio[1] == "mode":
1492                                         keys = ["red", "green", "yellow"]
1493                                         selection = self.audioChannel.getCurrentChannel()
1494                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1495                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1496                         else:
1497                                 del self.audioChannel
1498                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1499                                         self.audioTracks.selectTrack(audio[1])
1500                 else:
1501                         del self.audioChannel
1502                 del self.audioTracks
1503
1504         def modeSelected(self, mode):
1505                 if mode is not None:
1506                         self.audioChannel.selectChannel(mode[1])
1507                 del self.audioChannel
1508
1509 class InfoBarSubserviceSelection:
1510         def __init__(self):
1511                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1512                         {
1513                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1514                         })
1515
1516                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1517                         {
1518                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1519                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1520                         }, -1)
1521                 self["SubserviceQuickzapAction"].setEnabled(False)
1522
1523                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1524
1525                 self.bsel = None
1526
1527         def checkSubservicesAvail(self, ev):
1528                 if ev == iPlayableService.evUpdatedEventInfo:
1529                         service = self.session.nav.getCurrentService()
1530                         subservices = service and service.subServices()
1531                         if not subservices or subservices.getNumberOfSubservices() == 0:
1532                                 self["SubserviceQuickzapAction"].setEnabled(False)
1533
1534         def nextSubservice(self):
1535                 self.changeSubservice(+1)
1536
1537         def prevSubservice(self):
1538                 self.changeSubservice(-1)
1539
1540         def changeSubservice(self, direction):
1541                 service = self.session.nav.getCurrentService()
1542                 subservices = service and service.subServices()
1543                 n = subservices and subservices.getNumberOfSubservices()
1544                 if n and n > 0:
1545                         selection = -1
1546                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1547                         for x in range(n):
1548                                 if subservices.getSubservice(x).toString() == ref.toString():
1549                                         selection = x
1550                         if selection != -1:
1551                                 selection += direction
1552                                 if selection >= n:
1553                                         selection=0
1554                                 elif selection < 0:
1555                                         selection=n-1
1556                                 newservice = subservices.getSubservice(selection)
1557                                 if newservice.valid():
1558                                         del subservices
1559                                         del service
1560                                         self.session.nav.playService(newservice)
1561
1562         def subserviceSelection(self):
1563                 service = self.session.nav.getCurrentService()
1564                 subservices = service and service.subServices()
1565                 self.bouquets = self.servicelist.getBouquetList()
1566                 n = subservices and subservices.getNumberOfSubservices()
1567                 selection = 0
1568                 if n and n > 0:
1569                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1570                         tlist = []
1571                         for x in range(n):
1572                                 i = subservices.getSubservice(x)
1573                                 if i.toString() == ref.toString():
1574                                         selection = x
1575                                 tlist.append((i.getName(), i))
1576
1577                         if self.bouquets and len(self.bouquets):
1578                                 keys = ["red", "green", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1579                                 if config.usage.multibouquet.value:
1580                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1581                                 else:
1582                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1583                                 selection += 3
1584                         else:
1585                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1586                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1587                                 selection += 2
1588
1589                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys)
1590
1591         def subserviceSelected(self, service):
1592                 del self.bouquets
1593                 if not service is None:
1594                         if isinstance(service[1], str):
1595                                 if service[1] == "quickzap":
1596                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1597                                         self.session.open(SubservicesQuickzap, service[2])
1598                         else:
1599                                 self["SubserviceQuickzapAction"].setEnabled(True)
1600                                 self.session.nav.playService(service[1])
1601
1602         def addSubserviceToBouquetCallback(self, service):
1603                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1604                         self.selectedSubservice = service
1605                         if self.bouquets is None:
1606                                 cnt = 0
1607                         else:
1608                                 cnt = len(self.bouquets)
1609                         if cnt > 1: # show bouquet list
1610                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1611                         elif cnt == 1: # add to only one existing bouquet
1612                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1613                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1614
1615         def bouquetSelClosed(self, confirmed):
1616                 self.bsel = None
1617                 del self.selectedSubservice
1618                 if confirmed:
1619                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1620
1621         def addSubserviceToBouquet(self, dest):
1622                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1623                 if self.bsel:
1624                         self.bsel.close(True)
1625                 else:
1626                         del self.selectedSubservice
1627
1628 class InfoBarAdditionalInfo:
1629         def __init__(self):
1630                 self["NimA"] = Pixmap()
1631                 self["NimB"] = Pixmap()
1632                 self["NimA_Active"] = Pixmap()
1633                 self["NimB_Active"] = Pixmap()
1634
1635                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
1636                 self["TimeshiftPossible"] = self["RecordingPossible"]
1637                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1638
1639                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1640                 res_mgr = eDVBResourceManager.getInstance()
1641                 if res_mgr:
1642                         res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
1643
1644         def tunerUseMaskChanged(self, mask):
1645                 if mask&1:
1646                         self["NimA_Active"].show()
1647                 else:
1648                         self["NimA_Active"].hide()
1649                 if mask&2:
1650                         self["NimB_Active"].show()
1651                 else:
1652                         self["NimB_Active"].hide()
1653
1654         def checkTunerState(self, service):
1655                 info = service and service.frontendInfo()
1656                 feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
1657                 if feNumber is None:
1658                         self["NimA"].hide()
1659                         self["NimB"].hide()
1660                 elif feNumber == 0:
1661                         self["NimB"].hide()
1662                         self["NimA"].show()
1663                 elif feNumber == 1:
1664                         self["NimA"].hide()
1665                         self["NimB"].show()
1666
1667         def gotServiceEvent(self, ev):
1668                 service = self.session.nav.getCurrentService()
1669                 if ev == iPlayableService.evUpdatedInfo or ev == iPlayableService.evEnd:
1670                         self.checkTunerState(service)
1671
1672 class InfoBarNotifications:
1673         def __init__(self):
1674                 self.onExecBegin.append(self.checkNotifications)
1675                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1676                 self.onClose.append(self.__removeNotification)
1677         
1678         def __removeNotification(self):
1679                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1680         
1681         def checkNotificationsIfExecing(self):
1682                 if self.execing:
1683                         self.checkNotifications()
1684
1685         def checkNotifications(self):
1686                 if len(Notifications.notifications):
1687                         n = Notifications.notifications[0]
1688                         
1689                         Notifications.notifications = Notifications.notifications[1:]
1690                         cb = n[0]
1691
1692                         if n[3].has_key("onSessionOpenCallback"):
1693                                 n[3]["onSessionOpenCallback"]()
1694                                 del n[3]["onSessionOpenCallback"]
1695
1696                         if cb is not None:
1697                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1698                         else:
1699                                 dlg = self.session.open(n[1], *n[2], **n[3])
1700                         
1701                         # remember that this notification is currently active
1702                         d = (n[4], dlg)
1703                         Notifications.current_notifications.append(d)
1704                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1705
1706         def __notificationClosed(self, d):
1707                 Notifications.current_notifications.remove(d)
1708
1709 class InfoBarServiceNotifications:
1710         def __init__(self):
1711                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1712                         {
1713                                 iPlayableService.evEnd: self.serviceHasEnded
1714                         })
1715
1716         def serviceHasEnded(self):
1717                 print "service end!"
1718
1719                 try:
1720                         self.setSeekState(self.SEEK_STATE_PLAY)
1721                 except:
1722                         pass
1723
1724 class InfoBarCueSheetSupport:
1725         CUT_TYPE_IN = 0
1726         CUT_TYPE_OUT = 1
1727         CUT_TYPE_MARK = 2
1728         CUT_TYPE_LAST = 3
1729         
1730         ENABLE_RESUME_SUPPORT = False
1731         
1732         def __init__(self):
1733                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1734                         {
1735                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1736                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1737                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1738                         }, prio=1) 
1739                 
1740                 self.cut_list = [ ]
1741                 self.is_closing = False
1742                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1743                         {
1744                                 iPlayableService.evStart: self.__serviceStarted,
1745                         })
1746
1747         def __serviceStarted(self):
1748                 if self.is_closing:
1749                         return
1750                 print "new service started! trying to download cuts!"
1751                 self.downloadCuesheet()
1752                 
1753                 if self.ENABLE_RESUME_SUPPORT:
1754                         last = None
1755                         
1756                         for (pts, what) in self.cut_list:
1757                                 if what == self.CUT_TYPE_LAST:
1758                                         last = pts
1759                         
1760                         if last is not None:
1761                                 self.resume_point = last
1762                                 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1763
1764         def playLastCB(self, answer):
1765                 if answer == True:
1766                         seekable = self.__getSeekable()
1767                         if seekable is not None:
1768                                 seekable.seekTo(self.resume_point)
1769
1770         def __getSeekable(self):
1771                 service = self.session.nav.getCurrentService()
1772                 if service is None:
1773                         return None
1774                 return service.seek()
1775
1776         def cueGetCurrentPosition(self):
1777                 seek = self.__getSeekable()
1778                 if seek is None:
1779                         return None
1780                 r = seek.getPlayPosition()
1781                 if r[0]:
1782                         return None
1783                 return long(r[1])
1784
1785         def jumpPreviousNextMark(self, cmp, alternative=None):
1786                 current_pos = self.cueGetCurrentPosition()
1787                 if current_pos is None:
1788                         return
1789                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1790                 if mark is not None:
1791                         pts = mark[0]
1792                 elif alternative is not None:
1793                         pts = alternative
1794                 else:
1795                         return
1796
1797                 seekable = self.__getSeekable()
1798                 if seekable is not None:
1799                         seekable.seekTo(pts)
1800
1801         def jumpPreviousMark(self):
1802                 # we add 2 seconds, so if the play position is <2s after
1803                 # the mark, the mark before will be used
1804                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1805
1806         def jumpNextMark(self):
1807                 self.jumpPreviousNextMark(lambda x: x)
1808
1809         def getNearestCutPoint(self, pts, cmp=abs):
1810                 # can be optimized
1811                 nearest = None
1812                 for cp in self.cut_list:
1813                         diff = cmp(cp[0] - pts)
1814                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1815                                 nearest = cp
1816                 return nearest
1817
1818         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1819                 current_pos = self.cueGetCurrentPosition()
1820                 if current_pos is None:
1821                         print "not seekable"
1822                         return
1823                 
1824                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1825                 
1826                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1827                         if onlyreturn:
1828                                 return nearest_cutpoint
1829                         if not onlyadd:
1830                                 self.removeMark(nearest_cutpoint)
1831                 elif not onlyremove and not onlyreturn:
1832                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1833                 
1834                 if onlyreturn:
1835                         return None
1836
1837         def addMark(self, point):
1838                 insort(self.cut_list, point)
1839                 self.uploadCuesheet()
1840
1841         def removeMark(self, point):
1842                 self.cut_list.remove(point)
1843                 self.uploadCuesheet()
1844
1845         def __getCuesheet(self):
1846                 service = self.session.nav.getCurrentService()
1847                 if service is None:
1848                         return None
1849                 return service.cueSheet()
1850
1851         def uploadCuesheet(self):
1852                 cue = self.__getCuesheet()
1853
1854                 if cue is None:
1855                         print "upload failed, no cuesheet interface"
1856                         return
1857                 cue.setCutList(self.cut_list)
1858
1859         def downloadCuesheet(self):
1860                 cue = self.__getCuesheet()
1861
1862                 if cue is None:
1863                         print "upload failed, no cuesheet interface"
1864                         return
1865                 self.cut_list = cue.getCutList()
1866
1867 class InfoBarSummary(Screen):
1868         skin = """
1869         <screen position="0,0" size="132,64">
1870                 <widget source="CurrentTime" render="Label" position="56,46" size="82,18" font="Regular;16" >
1871                         <convert type="ClockToText">WithSeconds</convert>
1872                 </widget>
1873                 <widget source="CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
1874                         <convert type="ServiceName">Name</convert>
1875                 </widget>
1876         </screen>"""
1877
1878         def __init__(self, session, parent):
1879                 Screen.__init__(self, session)
1880                 self["CurrentService"] = CurrentService(self.session.nav)
1881                 self["CurrentTime"] = Clock()
1882
1883 class InfoBarSummarySupport:
1884         def __init__(self):
1885                 pass
1886         
1887         def createSummary(self):
1888                 return InfoBarSummary
1889
1890 class InfoBarTeletextPlugin:
1891         def __init__(self):
1892                 self.teletext_plugin = None
1893                 
1894                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1895                         self.teletext_plugin = p
1896                 
1897                 if self.teletext_plugin is not None:
1898                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1899                                 {
1900                                         "startTeletext": (self.startTeletext, _("View teletext..."))
1901                                 })
1902                 else:
1903                         print "no teletext plugin found!"
1904
1905         def startTeletext(self):
1906                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
1907
1908 class InfoBarSubtitleSupport(object):
1909         def __init__(self):
1910                 object.__init__(self)
1911                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
1912                 self.__subtitles_enabled = False
1913
1914                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1915                         {
1916                                 iPlayableService.evEnd: self.__serviceStopped,
1917                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
1918                         })
1919                 self.cached_subtitle_checked = False
1920
1921         def __serviceStopped(self):
1922                 self.subtitle_window.hide()
1923                 self.__subtitles_enabled = False
1924                 self.cached_subtitle_checked = False
1925
1926         def __updatedInfo(self):
1927                 if not self.cached_subtitle_checked:
1928                         subtitle = self.getCurrentServiceSubtitle()
1929                         self.cached_subtitle_checked = True
1930                         if subtitle:
1931                                 self.__selected_subtitle = subtitle.getCachedSubtitle()
1932                         if self.__selected_subtitle:
1933                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1934                                 self.subtitle_window.show()
1935                                 self.__subtitles_enabled = True
1936
1937         def getCurrentServiceSubtitle(self):
1938                 service = self.session.nav.getCurrentService()
1939                 return service and service.subtitle()
1940         
1941         def setSubtitlesEnable(self, enable=True):
1942                 subtitle = self.getCurrentServiceSubtitle()
1943                 if enable and self.__selected_subtitle is not None:
1944                         if subtitle and not self.__subtitles_enabled:
1945                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1946                                 self.subtitle_window.show()
1947                                 self.__subtitles_enabled = True
1948                 else:
1949                         if subtitle:
1950                                 subtitle.disableSubtitles(self.subtitle_window.instance)
1951                         self.__subtitles_enabled = False
1952                         self.subtitle_window.hide()
1953
1954         def setSelectedSubtitle(self, subtitle):
1955                 self.__selected_subtitle = subtitle
1956
1957         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
1958         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
1959
1960 class InfoBarServiceErrorPopupSupport:
1961         def __init__(self):
1962                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1963                         {
1964                                 iPlayableService.evTuneFailed: self.__tuneFailed,
1965                                 iPlayableService.evStart: self.__serviceStarted
1966                         })
1967                 self.__serviceStarted()
1968
1969         def __serviceStarted(self):
1970                 self.last_error = None
1971                 Notifications.RemovePopup(id = "ZapError")
1972
1973         def __tuneFailed(self):
1974                 service = self.session.nav.getCurrentService()
1975                 info = service and service.info()
1976                 error = info and info.getInfo(iServiceInformation.sDVBState)
1977                 
1978                 if error == self.last_error:
1979                         error = None
1980                 else:
1981                         self.last_error = error
1982
1983                 errors = {
1984                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
1985                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
1986                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
1987                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
1988                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
1989                         eDVBServicePMTHandler.eventNewProgramInfo: None,
1990                         eDVBServicePMTHandler.eventTuned: None,
1991                         eDVBServicePMTHandler.eventSOF: None,
1992                         eDVBServicePMTHandler.eventEOF: None
1993                 }
1994
1995                 error = errors.get(error) #this returns None when the key not exist in the dict
1996
1997                 if error is not None:
1998                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
1999                 else:
2000                         Notifications.RemovePopup(id = "ZapError")