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