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