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