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