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