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