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