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