use show_infobar_on_skip instead of on_zap when seeking
[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_32X = (0, 32, 0, ">> 32x")
617         SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
618         SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
619
620         SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
621         SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
622         SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
623         SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
624
625         SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
626         SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
627         SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
628
629         SEEK_STATE_EOF = (1, 0, 0, "END")
630
631         def __init__(self, actionmap = "InfobarSeekActions"):
632                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
633                         {
634                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
635                                 iPlayableService.evStart: self.__serviceStarted,
636
637                                 iPlayableService.evEOF: self.__evEOF,
638                                 iPlayableService.evSOF: self.__evSOF,
639                         })
640
641                 class InfoBarSeekActionMap(HelpableActionMap):
642                         def __init__(self, screen, *args, **kwargs):
643                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
644                                 self.screen = screen
645
646                         def action(self, contexts, action):
647                                 print "action:", action
648                                 if action[:5] == "seek:":
649                                         time = int(action[5:])
650                                         self.screen.seekRelative(time * 90000)
651                                         if config.usage.show_infobar_on_skip.value:
652                                                 self.screen.showAfterSeek()
653                                         return 1
654                                 else:
655                                         return HelpableActionMap.action(self, contexts, action)
656
657                 self["SeekActions"] = InfoBarSeekActionMap(self, actionmap, 
658                         {
659                                 "playpauseService": self.playpauseService,
660                                 "pauseService": (self.pauseService, _("pause")),
661                                 "unPauseService": (self.unPauseService, _("continue")),
662
663                                 "seekFwd": (self.seekFwd, _("skip forward")),
664                                 "seekFwdManual": (self.seekFwdManual, _("skip forward (enter time)")),
665                                 "seekBack": (self.seekBack, _("skip backward")),
666                                 "seekBackManual": (self.seekBackManual, _("skip backward (enter time)")),
667                                 
668                                 "seekFwdDef": (self.seekFwdDef, _("skip forward (self defined)")),
669                                 "seekBackDef": (self.seekBackDef, _("skip backward (self defined)"))
670                         }, prio=-1)
671                         # give them a little more priority to win over color buttons
672
673                 self["SeekActions"].setEnabled(False)
674
675                 self.seekstate = self.SEEK_STATE_PLAY
676                 
677                 self.seek_flag = True
678
679                 self.onPlayStateChanged = [ ]
680
681                 self.lockedBecauseOfSkipping = False
682
683                 self.__seekableStatusChanged()
684
685         def showAfterSeek(self):
686                 if isinstance(self, InfoBarShowHide):
687                         self.doShow()
688
689         def up(self):
690                 pass
691
692         def down(self):
693                 pass
694
695         def getSeek(self):
696                 service = self.session.nav.getCurrentService()
697                 if service is None:
698                         return None
699
700                 seek = service.seek()
701
702                 if seek is None or not seek.isCurrentlySeekable():
703                         return None
704
705                 return seek
706
707         def isSeekable(self):
708                 if self.getSeek() is None:
709                         return False
710                 return True
711
712         def __seekableStatusChanged(self):
713                 print "seekable status changed!"
714                 if not self.isSeekable():
715                         self["SeekActions"].setEnabled(False)
716                         print "not seekable, return to play"
717                         self.setSeekState(self.SEEK_STATE_PLAY)
718                 else:
719                         self["SeekActions"].setEnabled(True)
720                         print "seekable"
721
722         def __serviceStarted(self):
723                 self.seekstate = self.SEEK_STATE_PLAY
724                 self.__seekableStatusChanged()
725
726         def setSeekState(self, state):
727                 service = self.session.nav.getCurrentService()
728
729                 if service is None:
730                         return False
731
732                 if not self.isSeekable():
733                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
734                                 state = self.SEEK_STATE_PLAY
735
736                 pauseable = service.pause()
737
738                 if pauseable is None:
739                         print "not pauseable."
740                         state = self.SEEK_STATE_PLAY
741
742                 oldstate = self.seekstate
743                 self.seekstate = state
744
745                 for i in range(3):
746                         if oldstate[i] != self.seekstate[i]:
747                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
748
749                 for c in self.onPlayStateChanged:
750                         c(self.seekstate)
751
752                 self.checkSkipShowHideLock()
753
754                 return True
755
756         def playpauseService(self):
757                 if self.seekstate != self.SEEK_STATE_PLAY:
758                         self.unPauseService()
759                 else:
760                         self.pauseService()
761
762         def pauseService(self):
763                 if self.seekstate == self.SEEK_STATE_PAUSE:
764                         print "pause, but in fact unpause"
765                         self.unPauseService()
766                 else:
767                         if self.seekstate == self.SEEK_STATE_PLAY:
768                                 print "yes, playing."
769                         else:
770                                 print "no", self.seekstate
771                         print "pause"
772                         self.setSeekState(self.SEEK_STATE_PAUSE);
773
774         def unPauseService(self):
775                 print "unpause"
776                 if self.seekstate == self.SEEK_STATE_PLAY:
777                         return 0
778                 self.setSeekState(self.SEEK_STATE_PLAY)
779
780         def doSeek(self, seektime):
781                 print "doseek", seektime
782                 service = self.session.nav.getCurrentService()
783                 if service is None:
784                         return
785
786                 seekable = self.getSeek()
787                 if seekable is None:
788                         return
789
790                 seekable.seekTo(90 * seektime)
791
792         def seekFwd(self):
793                 lookup = {
794                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
795                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
796                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
797                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
798                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
799                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
800                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
801                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
802                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
803                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
804                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
805                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
806                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
807                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
808                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER,
809                                 self.SEEK_STATE_EOF: self.SEEK_STATE_EOF,
810                         }
811                 self.setSeekState(lookup[self.seekstate])
812
813         def seekBack(self):
814                 lookup = {
815                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
816                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
817                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
818                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
819                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
820                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
821                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
822                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
823                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
824                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
825                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
826                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
827                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
828                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
829                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE,
830                                 self.SEEK_STATE_EOF: self.SEEK_STATE_BACK_16X,
831                         }
832                 self.setSeekState(lookup[self.seekstate])
833
834                 if self.seekstate == self.SEEK_STATE_PAUSE:
835                         seekable = self.getSeek()
836                         if seekable is not None:
837                                 seekable.seekRelative(-1, 3)
838
839         def seekFwdDef(self):
840                 self.seek_flag = False
841                 seconds = config.usage.self_defined_seek.value
842                 print "Seek", seconds, "seconds self defined forward"
843                 seekable = self.getSeek()
844                 if seekable is not None:
845                         seekable.seekRelative(1, seconds * 90000)
846                 
847         def seekBackDef(self):
848                 self.seek_flag = False
849                 seconds = config.usage.self_defined_seek.value
850                 print "Seek", seconds, "seconds self defined backward"
851                 seekable = self.getSeek()
852                 if seekable is not None:
853                         seekable.seekRelative(1, 0 - seconds * 90000)
854                 
855         def seekFwdManual(self):
856                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
857
858         def fwdSeekTo(self, minutes):
859                 print "Seek", minutes, "minutes forward"
860                 if minutes != 0:
861                         seekable = self.getSeek()
862                         if seekable is not None:
863                                 seekable.seekRelative(1, minutes * 60 * 90000)
864
865         def seekBackManual(self):
866                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
867
868         def rwdSeekTo(self, minutes):
869                 print "rwdSeekTo"
870                 self.fwdSeekTo(0 - minutes)
871
872         def checkSkipShowHideLock(self):
873                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
874
875                 if config.usage.show_infobar_on_skip.value:
876                         if self.lockedBecauseOfSkipping and not wantlock:
877                                 self.unlockShow()
878                                 self.lockedBecauseOfSkipping = False
879
880                         if wantlock and not self.lockedBecauseOfSkipping:
881                                 self.lockShow()
882                                 self.lockedBecauseOfSkipping = True
883
884         def __evEOF(self):
885                 if self.seekstate == self.SEEK_STATE_EOF:
886                         return
887                 if self.seekstate[1] < 0: # SEEK_STATE_BACK_*X
888                         print "end of stream while seeking back, ignoring."
889                         return
890
891                 # if we are seeking, we try to end up ~1s before the end, and pause there.
892                 if not self.seekstate in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
893                         self.setSeekState(self.SEEK_STATE_EOF)
894                         self.seekRelativeToEnd(-90000)
895                 else:
896                         self.setSeekState(self.SEEK_STATE_EOF)
897
898         def __evSOF(self):
899                 self.setSeekState(self.SEEK_STATE_PLAY)
900                 self.doSeek(0)
901
902         def seekRelative(self, diff):
903                 if self.seek_flag == True:
904                         seekable = self.getSeek()
905                         if seekable is not None:
906                                 print "seekRelative: res:", seekable.seekRelative(1, diff)
907                         else:
908                                 print "seek failed!"
909                 else:
910                         self.seek_flag = True
911
912         def seekRelativeToEnd(self, diff):
913                 assert diff <= 0, "diff is expected to be negative!"
914
915                 # might sound like an evil hack, but:
916                 # if we seekRelativeToEnd(0), we expect to be at the end, which is what we want,
917                 # and we don't get that by passing 0 here (it would seek to begin).
918                 if diff == 0:
919                         diff = -1
920
921                 # relative-to-end seeking is implemented as absolutes seeks with negative time
922                 self.seekAbsolute(diff)
923
924         def seekAbsolute(self, abs):
925                 seekable = self.getSeek()
926                 if seekable is not None:
927                         seekable.seekTo(abs)
928
929 from Screens.PVRState import PVRState, TimeshiftState
930
931 class InfoBarPVRState:
932         def __init__(self, screen=PVRState):
933                 self.onPlayStateChanged.append(self.__playStateChanged)
934                 self.pvrStateDialog = self.session.instantiateDialog(screen)
935                 self.onShow.append(self._mayShow)
936                 self.onHide.append(self.pvrStateDialog.hide)
937
938         def _mayShow(self):
939                 if self.execing and self.seekstate != self.SEEK_STATE_PLAY:
940                         self.pvrStateDialog.show()
941
942         def __playStateChanged(self, state):
943                 playstateString = state[3]
944                 self.pvrStateDialog["state"].setText(playstateString)
945                 self._mayShow()
946
947 class InfoBarTimeshiftState(InfoBarPVRState):
948         def __init__(self):
949                 InfoBarPVRState.__init__(self, screen=TimeshiftState)
950
951         def _mayShow(self):
952                 if self.execing and self.timeshift_enabled:
953                         self.pvrStateDialog.show()
954
955 class InfoBarShowMovies:
956
957         # i don't really like this class. 
958         # it calls a not further specified "movie list" on up/down/movieList,
959         # so this is not more than an action map
960         def __init__(self):
961                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
962                         {
963                                 "movieList": (self.showMovies, _("movie list")),
964                                 "up": (self.showMovies, _("movie list")),
965                                 "down": (self.showMovies, _("movie list"))
966                         })
967
968 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
969
970 # Hrmf.
971 #
972 # Timeshift works the following way:
973 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
974 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
975 # - user presses "yellow" button.         FILE     record      PAUSE              enable                disable              enable
976 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
977 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
978 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
979 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
980 #
981
982 # in other words:
983 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
984 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
985 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
986 # - the user can now PVR around
987 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
988 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
989 # after!
990 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
991 # - if the user rewinds, or press pause, timeshift will be activated again
992
993 # note that a timeshift can be enabled ("recording") and
994 # activated (currently time-shifting).
995
996 class InfoBarTimeshift:
997         def __init__(self):
998                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
999                         {
1000                                 "timeshiftStart": (self.startTimeshift, _("start timeshift")),  # the "yellow key"
1001                                 "timeshiftStop": (self.stopTimeshift, _("stop timeshift"))      # currently undefined :), probably 'TV'
1002                         }, prio=1)
1003                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
1004                         {
1005                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "rewind key"
1006                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "pause key"
1007                         }, prio=-1) # priority over record
1008
1009                 self.timeshift_enabled = 0
1010                 self.timeshift_state = 0
1011                 self.ts_rewind_timer = eTimer()
1012                 self.ts_rewind_timer.timeout.get().append(self.rewindService)
1013
1014                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1015                         {
1016                                 iPlayableService.evStart: self.__serviceStarted,
1017                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
1018                         })
1019
1020         def getTimeshift(self):
1021                 service = self.session.nav.getCurrentService()
1022                 return service and service.timeshift()
1023
1024         def startTimeshift(self):
1025                 print "enable timeshift"
1026                 ts = self.getTimeshift()
1027                 if ts is None:
1028                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
1029                         print "no ts interface"
1030                         return 0
1031
1032                 if self.timeshift_enabled:
1033                         print "hu, timeshift already enabled?"
1034                 else:
1035                         if not ts.startTimeshift():
1036                                 self.timeshift_enabled = 1
1037
1038                                 # we remove the "relative time" for now.
1039                                 #self.pvrStateDialog["timeshift"].setRelative(time.time())
1040
1041                                 # PAUSE.
1042                                 #self.setSeekState(self.SEEK_STATE_PAUSE)
1043                                 self.activateTimeshiftEnd(False)
1044
1045                                 # enable the "TimeshiftEnableActions", which will override
1046                                 # the startTimeshift actions
1047                                 self.__seekableStatusChanged()
1048                         else:
1049                                 print "timeshift failed"
1050
1051         def stopTimeshift(self):
1052                 if not self.timeshift_enabled:
1053                         return 0
1054                 print "disable timeshift"
1055                 ts = self.getTimeshift()
1056                 if ts is None:
1057                         return 0
1058                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
1059
1060         def stopTimeshiftConfirmed(self, confirmed):
1061                 if not confirmed:
1062                         return
1063
1064                 ts = self.getTimeshift()
1065                 if ts is None:
1066                         return
1067
1068                 ts.stopTimeshift()
1069                 self.timeshift_enabled = 0
1070
1071                 # disable actions
1072                 self.__seekableStatusChanged()
1073
1074         # activates timeshift, and seeks to (almost) the end
1075         def activateTimeshiftEnd(self, back = True):
1076                 ts = self.getTimeshift()
1077                 print "activateTimeshiftEnd"
1078
1079                 if ts is None:
1080                         return
1081
1082                 if ts.isTimeshiftActive():
1083                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
1084                         self.pauseService()
1085                 else:
1086                         print "play, ..."
1087                         ts.activateTimeshift() # activate timeshift will automatically pause
1088                         self.setSeekState(self.SEEK_STATE_PAUSE)
1089                         self.seekRelativeToEnd(-90000) # seek approx. 1 sec before end
1090
1091                 if back:
1092                         self.ts_rewind_timer.start(200, 1)
1093
1094         def rewindService(self):
1095                 self.setSeekState(self.SEEK_STATE_BACK_16X)
1096
1097         # same as activateTimeshiftEnd, but pauses afterwards.
1098         def activateTimeshiftEndAndPause(self):
1099                 print "activateTimeshiftEndAndPause"
1100                 #state = self.seekstate
1101                 self.activateTimeshiftEnd(False)
1102
1103         def __seekableStatusChanged(self):
1104                 enabled = False
1105
1106                 print "self.isSeekable", self.isSeekable()
1107                 print "self.timeshift_enabled", self.timeshift_enabled
1108
1109                 # when this service is not seekable, but timeshift
1110                 # is enabled, this means we can activate
1111                 # the timeshift
1112                 if not self.isSeekable() and self.timeshift_enabled:
1113                         enabled = True
1114
1115                 print "timeshift activate:", enabled
1116                 self["TimeshiftActivateActions"].setEnabled(enabled)
1117
1118         def __serviceStarted(self):
1119                 self.timeshift_enabled = False
1120                 self.__seekableStatusChanged()
1121
1122 from Screens.PiPSetup import PiPSetup
1123
1124 class InfoBarExtensions:
1125         EXTENSION_SINGLE = 0
1126         EXTENSION_LIST = 1
1127
1128         def __init__(self):
1129                 self.list = []
1130
1131                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
1132                         {
1133                                 "extensions": (self.showExtensionSelection, _("view extensions...")),
1134                         })
1135
1136         def addExtension(self, extension, key = None, type = EXTENSION_SINGLE):
1137                 self.list.append((type, extension, key))
1138
1139         def updateExtension(self, extension, key = None):
1140                 self.extensionsList.append(extension)
1141                 if key is not None:
1142                         if self.extensionKeys.has_key(key):
1143                                 key = None
1144
1145                 if key is None:
1146                         for x in self.availableKeys:
1147                                 if not self.extensionKeys.has_key(x):
1148                                         key = x
1149                                         break
1150
1151                 if key is not None:
1152                         self.extensionKeys[key] = len(self.extensionsList) - 1
1153
1154         def updateExtensions(self):
1155                 self.extensionsList = []
1156                 self.availableKeys = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "red", "green", "yellow", "blue" ]
1157                 self.extensionKeys = {}
1158                 for x in self.list:
1159                         if x[0] == self.EXTENSION_SINGLE:
1160                                 self.updateExtension(x[1], x[2])
1161                         else:
1162                                 for y in x[1]():
1163                                         self.updateExtension(y[0], y[1])
1164
1165
1166         def showExtensionSelection(self):
1167                 self.updateExtensions()
1168                 extensionsList = self.extensionsList[:]
1169                 keys = []
1170                 list = []
1171                 for x in self.availableKeys:
1172                         if self.extensionKeys.has_key(x):
1173                                 entry = self.extensionKeys[x]
1174                                 extension = self.extensionsList[entry]
1175                                 if extension[2]():
1176                                         name = str(extension[0]())
1177                                         list.append((extension[0](), extension))
1178                                         keys.append(x)
1179                                         extensionsList.remove(extension)
1180                                 else:
1181                                         extensionsList.remove(extension)
1182                 for x in extensionsList:
1183                         list.append((x[0](), x))
1184                 keys += [""] * len(extensionsList)
1185                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list, keys = keys)
1186
1187         def extensionCallback(self, answer):
1188                 if answer is not None:
1189                         answer[1][1]()
1190
1191 from Tools.BoundFunction import boundFunction
1192
1193 # depends on InfoBarExtensions
1194 from Components.PluginComponent import plugins
1195
1196 class InfoBarPlugins:
1197         def __init__(self):
1198                 self.addExtension(extension = self.getPluginList, type = InfoBarExtensions.EXTENSION_LIST)
1199
1200         def getPluginName(self, name):
1201                 return name
1202
1203         def getPluginList(self):
1204                 list = []
1205                 for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU):
1206                         list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None))
1207                 return list
1208
1209         def runPlugin(self, plugin):
1210                 plugin(session = self.session, servicelist = self.servicelist)
1211
1212 # depends on InfoBarExtensions
1213 class InfoBarSleepTimer:
1214         def __init__(self):
1215                 self.addExtension((self.getSleepTimerName, self.showSleepTimerSetup, self.available), "1")
1216
1217         def available(self):
1218                 return True
1219
1220         def getSleepTimerName(self):
1221                 return _("Sleep Timer")
1222
1223         def showSleepTimerSetup(self):
1224                 self.session.open(SleepTimerEdit)
1225
1226 # depends on InfoBarExtensions
1227 class InfoBarPiP:
1228         def __init__(self):
1229                 self.session.pipshown = False
1230
1231                 self.addExtension((self.getShowHideName, self.showPiP, self.available), "blue")
1232                 self.addExtension((self.getMoveName, self.movePiP, self.pipShown), "green")
1233                 self.addExtension((self.getSwapName, self.swapPiP, self.pipShown), "yellow")
1234
1235         def available(self):
1236                 return True
1237
1238         def pipShown(self):
1239                 return self.session.pipshown
1240
1241         def pipHandles0Action(self):
1242                 return self.pipShown() and config.usage.pip_zero_button.value != "standard"
1243
1244         def getShowHideName(self):
1245                 if self.session.pipshown:
1246                         return _("Disable Picture in Picture")
1247                 else:
1248                         return _("Activate Picture in Picture")
1249
1250         def getSwapName(self):
1251                 return _("Swap Services")
1252
1253         def getMoveName(self):
1254                 return _("Move Picture in Picture")
1255
1256         def showPiP(self):
1257                 if self.session.pipshown:
1258                         del self.session.pip
1259                         self.session.pipshown = False
1260                 else:
1261                         self.session.pip = self.session.instantiateDialog(PictureInPicture)
1262                         self.session.pip.show()
1263                         newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1264                         if self.session.pip.playService(newservice):
1265                                 self.session.pipshown = True
1266                                 self.session.pip.servicePath = self.servicelist.getCurrentServicePath()
1267                         else:
1268                                 self.session.pipshown = False
1269                                 del self.session.pip
1270                         self.session.nav.playService(newservice)
1271
1272         def swapPiP(self):
1273                 swapservice = self.session.nav.getCurrentlyPlayingServiceReference()
1274                 if self.session.pip.servicePath:
1275                         servicepath = self.servicelist.getCurrentServicePath()
1276                         ref=servicepath[len(servicepath)-1]
1277                         pipref=self.session.pip.getCurrentService()
1278                         self.session.pip.playService(swapservice)
1279                         self.servicelist.setCurrentServicePath(self.session.pip.servicePath)
1280                         if pipref.toString() != ref.toString(): # is a subservice ?
1281                                 self.session.nav.stopService() # stop portal
1282                                 self.session.nav.playService(pipref) # start subservice
1283                         self.session.pip.servicePath=servicepath
1284
1285         def movePiP(self):
1286                 self.session.open(PiPSetup, pip = self.session.pip)
1287
1288         def pipDoHandle0Action(self):
1289                 use = config.usage.pip_zero_button.value
1290                 if "swap" == use:
1291                         self.swapPiP()
1292                 elif "swapstop" == use:
1293                         self.swapPiP()
1294                         self.showPiP()
1295                 elif "stop" == use:
1296                         self.showPiP()
1297
1298 from RecordTimer import parseEvent
1299
1300 class InfoBarInstantRecord:
1301         """Instant Record - handles the instantRecord action in order to 
1302         start/stop instant records"""
1303         def __init__(self):
1304                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1305                         {
1306                                 "instantRecord": (self.instantRecord, _("Instant Record...")),
1307                         })
1308                 self.recording = []
1309                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1310                 self["BlinkingPoint"].hide()
1311                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1312
1313         def stopCurrentRecording(self, entry = -1):
1314                 if entry is not None and entry != -1:
1315                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1316                         self.recording.remove(self.recording[entry])
1317
1318         def startInstantRecording(self, limitEvent = False):
1319                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1320
1321                 # try to get event info
1322                 event = None
1323                 try:
1324                         service = self.session.nav.getCurrentService()
1325                         epg = eEPGCache.getInstance()
1326                         event = epg.lookupEventTime(serviceref, -1, 0)
1327                         if event is None:
1328                                 info = service.info()
1329                                 ev = info.getEvent(0)
1330                                 event = ev
1331                 except:
1332                         pass
1333
1334                 begin = time()
1335                 end = time() + 3600 * 10
1336                 name = "instant record"
1337                 description = ""
1338                 eventid = None
1339
1340                 if event is not None:
1341                         curEvent = parseEvent(event)
1342                         name = curEvent[2]
1343                         description = curEvent[3]
1344                         eventid = curEvent[4]
1345                         if limitEvent:
1346                                 end = curEvent[1]
1347                 else:
1348                         if limitEvent:
1349                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1350
1351                 data = (begin, end, name, description, eventid)
1352
1353                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1354                 recording.dontSave = True
1355                 self.recording.append(recording)
1356
1357                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1358
1359         def isInstantRecordRunning(self):
1360                 print "self.recording:", self.recording
1361                 if len(self.recording) > 0:
1362                         for x in self.recording:
1363                                 if x.isRunning():
1364                                         return True
1365                 return False
1366
1367         def recordQuestionCallback(self, answer):
1368                 print "pre:\n", self.recording
1369
1370                 if answer is None or answer[1] == "no":
1371                         return
1372                 list = []
1373                 recording = self.recording[:]
1374                 for x in recording:
1375                         if not x in self.session.nav.RecordTimer.timer_list:
1376                                 self.recording.remove(x)
1377                         elif x.dontSave and x.isRunning():
1378                                 list.append((x, False))
1379
1380                 if answer[1] == "changeduration":
1381                         if len(self.recording) == 1:
1382                                 self.changeDuration(0)
1383                         else:
1384                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1385                 elif answer[1] == "changeendtime":
1386                         if len(self.recording) == 1:
1387                                 self.setEndtime(0)
1388                         else:
1389                                 self.session.openWithCallback(self.setEndTime, TimerSelection, list)
1390                 elif answer[1] == "stop":
1391                         if len(self.recording) == 1:
1392                                 self.stopCurrentRecording(0)
1393                         else:
1394                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1395                 elif answer[1] in ( "indefinitely" , "manualduration", "manualendtime", "event"):
1396                         self.startInstantRecording(limitEvent = answer[1] in ("event", "manualendtime") or False)
1397                         if answer[1] == "manualduration":
1398                                 self.changeDuration(len(self.recording)-1)
1399                         elif answer[1] == "manualendtime":
1400                                 self.setEndtime(len(self.recording)-1)
1401                 print "after:\n", self.recording
1402
1403         def setEndtime(self, entry):
1404                 if entry is not None:
1405                         self.selectedEntry = entry
1406                         self.endtime=ConfigClock(default = self.recording[self.selectedEntry].end)
1407                         dlg = self.session.openWithCallback(self.TimeDateInputClosed, TimeDateInput, self.endtime)
1408                         dlg.setTitle(_("Please change recording endtime"))
1409
1410         def TimeDateInputClosed(self, ret):
1411                 if len(ret) > 1:
1412                         if ret[0]:
1413                                 localendtime = localtime(ret[1])
1414                                 print "stopping recording at", strftime("%c", localendtime)
1415                                 self.recording[self.selectedEntry].end = ret[1]
1416                                 self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1417
1418         def changeDuration(self, entry):
1419                 if entry is not None:
1420                         self.selectedEntry = entry
1421                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1422
1423         def inputCallback(self, value):
1424                 if value is not None:
1425                         print "stopping recording after", int(value), "minutes."
1426                         self.recording[self.selectedEntry].end = time() + 60 * int(value)
1427                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1428
1429         def instantRecord(self):
1430                 try:
1431                         stat = os_stat(resolveFilename(SCOPE_HDD))
1432                 except:
1433                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1434                         return
1435
1436                 if self.isInstantRecordRunning():
1437                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1438                                 title=_("A recording is currently running.\nWhat do you want to do?"), \
1439                                 list=[(_("stop recording"), "stop"), \
1440                                 (_("change recording (duration)"), "changeduration"), \
1441                                 (_("change recording (endtime)"), "changeendtime"), \
1442                                 (_("add recording (indefinitely)"), "indefinitely"), \
1443                                 (_("add recording (stop after current event)"), "event"), \
1444                                 (_("add recording (enter recording duration)"), "manualduration"), \
1445                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1446                                 (_("do nothing"), "no")])
1447                 else:
1448                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, \
1449                                 title=_("Start recording?"), \
1450                                 list=[(_("add recording (indefinitely)"), "indefinitely"), \
1451                                 (_("add recording (stop after current event)"), "event"), \
1452                                 (_("add recording (enter recording duration)"), "manualduration"), \
1453                                 (_("add recording (enter recording endtime)"), "manualendtime"), \
1454                                 (_("don't record"), "no")])
1455
1456 from Tools.ISO639 import LanguageCodes
1457
1458 class InfoBarAudioSelection:
1459         def __init__(self):
1460                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1461                         {
1462                                 "audioSelection": (self.audioSelection, _("Audio Options...")),
1463                         })
1464
1465         def audioSelection(self):
1466                 service = self.session.nav.getCurrentService()
1467                 audio = service and service.audioTracks()
1468                 self.audioTracks = audio
1469                 n = audio and audio.getNumberOfTracks() or 0
1470                 keys = [ "red", "", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] + [""]*n
1471                 tlist = []
1472                 if n > 0:
1473                         self.audioChannel = service.audioChannel()
1474
1475                         for x in range(n):
1476                                 i = audio.getTrackInfo(x)
1477                                 language = i.getLanguage()
1478                                 description = i.getDescription()
1479
1480                                 if LanguageCodes.has_key(language):
1481                                         language = LanguageCodes[language][0]
1482
1483                                 if len(description):
1484                                         description += " (" + language + ")"
1485                                 else:
1486                                         description = language
1487
1488                                 tlist.append((description, x))
1489
1490                         selectedAudio = audio.getCurrentTrack()
1491                         tlist.sort(key=lambda x: x[0])
1492
1493                         selection = 2
1494                         for x in tlist:
1495                                 if x[1] != selectedAudio:
1496                                         selection += 1
1497                                 else:
1498                                         break
1499
1500                         tlist = [([_("Left"), _("Stereo"), _("Right")][self.audioChannel.getCurrentChannel()], "mode"), ("--", "")] + tlist
1501                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection, keys = keys)
1502                 else:
1503                         del self.audioTracks
1504
1505         def audioSelected(self, audio):
1506                 if audio is not None:
1507                         if isinstance(audio[1], str):
1508                                 if audio[1] == "mode":
1509                                         keys = ["red", "green", "yellow"]
1510                                         selection = self.audioChannel.getCurrentChannel()
1511                                         tlist = [(_("left"), 0), (_("stereo"), 1), (_("right"), 2)]
1512                                         self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys)
1513                         else:
1514                                 del self.audioChannel
1515                                 if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]:
1516                                         self.audioTracks.selectTrack(audio[1])
1517                 else:
1518                         del self.audioChannel
1519                 del self.audioTracks
1520
1521         def modeSelected(self, mode):
1522                 if mode is not None:
1523                         self.audioChannel.selectChannel(mode[1])
1524                 del self.audioChannel
1525
1526 class InfoBarSubserviceSelection:
1527         def __init__(self):
1528                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1529                         {
1530                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1531                         })
1532
1533                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1534                         {
1535                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1536                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1537                         }, -1)
1538                 self["SubserviceQuickzapAction"].setEnabled(False)
1539
1540                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1541
1542                 self.bsel = None
1543
1544         def checkSubservicesAvail(self, ev):
1545                 if ev == iPlayableService.evUpdatedEventInfo:
1546                         service = self.session.nav.getCurrentService()
1547                         subservices = service and service.subServices()
1548                         if not subservices or subservices.getNumberOfSubservices() == 0:
1549                                 self["SubserviceQuickzapAction"].setEnabled(False)
1550
1551         def nextSubservice(self):
1552                 self.changeSubservice(+1)
1553
1554         def prevSubservice(self):
1555                 self.changeSubservice(-1)
1556
1557         def changeSubservice(self, direction):
1558                 service = self.session.nav.getCurrentService()
1559                 subservices = service and service.subServices()
1560                 n = subservices and subservices.getNumberOfSubservices()
1561                 if n and n > 0:
1562                         selection = -1
1563                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1564                         for x in range(n):
1565                                 if subservices.getSubservice(x).toString() == ref.toString():
1566                                         selection = x
1567                         if selection != -1:
1568                                 selection += direction
1569                                 if selection >= n:
1570                                         selection=0
1571                                 elif selection < 0:
1572                                         selection=n-1
1573                                 newservice = subservices.getSubservice(selection)
1574                                 if newservice.valid():
1575                                         del subservices
1576                                         del service
1577                                         self.session.nav.playService(newservice)
1578
1579         def subserviceSelection(self):
1580                 service = self.session.nav.getCurrentService()
1581                 subservices = service and service.subServices()
1582                 self.bouquets = self.servicelist.getBouquetList()
1583                 n = subservices and subservices.getNumberOfSubservices()
1584                 selection = 0
1585                 if n and n > 0:
1586                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1587                         tlist = []
1588                         for x in range(n):
1589                                 i = subservices.getSubservice(x)
1590                                 if i.toString() == ref.toString():
1591                                         selection = x
1592                                 tlist.append((i.getName(), i))
1593
1594                         if self.bouquets and len(self.bouquets):
1595                                 keys = ["red", "green", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1596                                 if config.usage.multibouquet.value:
1597                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to bouquet"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1598                                 else:
1599                                         tlist = [(_("Quickzap"), "quickzap", service.subServices()), (_("Add to favourites"), "CALLFUNC", self.addSubserviceToBouquetCallback), ("--", "")] + tlist
1600                                 selection += 3
1601                         else:
1602                                 tlist = [(_("Quickzap"), "quickzap", service.subServices()), ("--", "")] + tlist
1603                                 keys = ["red", "",  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" ] + [""] * n
1604                                 selection += 2
1605
1606                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection, keys = keys)
1607
1608         def subserviceSelected(self, service):
1609                 del self.bouquets
1610                 if not service is None:
1611                         if isinstance(service[1], str):
1612                                 if service[1] == "quickzap":
1613                                         from Screens.SubservicesQuickzap import SubservicesQuickzap
1614                                         self.session.open(SubservicesQuickzap, service[2])
1615                         else:
1616                                 self["SubserviceQuickzapAction"].setEnabled(True)
1617                                 self.session.nav.playService(service[1])
1618
1619         def addSubserviceToBouquetCallback(self, service):
1620                 if len(service) > 1 and isinstance(service[1], eServiceReference):
1621                         self.selectedSubservice = service
1622                         if self.bouquets is None:
1623                                 cnt = 0
1624                         else:
1625                                 cnt = len(self.bouquets)
1626                         if cnt > 1: # show bouquet list
1627                                 self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, self.bouquets, self.addSubserviceToBouquet)
1628                         elif cnt == 1: # add to only one existing bouquet
1629                                 self.addSubserviceToBouquet(self.bouquets[0][1])
1630                                 self.session.open(MessageBox, _("Service has been added to the favourites."), MessageBox.TYPE_INFO)
1631
1632         def bouquetSelClosed(self, confirmed):
1633                 self.bsel = None
1634                 del self.selectedSubservice
1635                 if confirmed:
1636                         self.session.open(MessageBox, _("Service has been added to the selected bouquet."), MessageBox.TYPE_INFO)
1637
1638         def addSubserviceToBouquet(self, dest):
1639                 self.servicelist.addServiceToBouquet(dest, self.selectedSubservice[1])
1640                 if self.bsel:
1641                         self.bsel.close(True)
1642                 else:
1643                         del self.selectedSubservice
1644
1645 class InfoBarAdditionalInfo:
1646         def __init__(self):
1647                 self["NimA"] = Pixmap()
1648                 self["NimB"] = Pixmap()
1649                 self["NimA_Active"] = Pixmap()
1650                 self["NimB_Active"] = Pixmap()
1651
1652                 self["RecordingPossible"] = Boolean(fixed=harddiskmanager.HDDCount() > 0)
1653                 self["TimeshiftPossible"] = self["RecordingPossible"]
1654                 self["ExtensionsAvailable"] = Boolean(fixed=1)
1655
1656                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1657                 res_mgr = eDVBResourceManager.getInstance()
1658                 if res_mgr:
1659                         res_mgr.frontendUseMaskChanged.get().append(self.tunerUseMaskChanged)
1660
1661         def tunerUseMaskChanged(self, mask):
1662                 if mask&1:
1663                         self["NimA_Active"].show()
1664                 else:
1665                         self["NimA_Active"].hide()
1666                 if mask&2:
1667                         self["NimB_Active"].show()
1668                 else:
1669                         self["NimB_Active"].hide()
1670
1671         def checkTunerState(self, service):
1672                 info = service and service.frontendInfo()
1673                 feNumber = info and info.getFrontendInfo(iFrontendInformation.frontendNumber)
1674                 if feNumber is None:
1675                         self["NimA"].hide()
1676                         self["NimB"].hide()
1677                 elif feNumber == 0:
1678                         self["NimB"].hide()
1679                         self["NimA"].show()
1680                 elif feNumber == 1:
1681                         self["NimA"].hide()
1682                         self["NimB"].show()
1683
1684         def gotServiceEvent(self, ev):
1685                 service = self.session.nav.getCurrentService()
1686                 if ev == iPlayableService.evUpdatedInfo or ev == iPlayableService.evEnd:
1687                         self.checkTunerState(service)
1688
1689 class InfoBarNotifications:
1690         def __init__(self):
1691                 self.onExecBegin.append(self.checkNotifications)
1692                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1693                 self.onClose.append(self.__removeNotification)
1694
1695         def __removeNotification(self):
1696                 Notifications.notificationAdded.remove(self.checkNotificationsIfExecing)
1697
1698         def checkNotificationsIfExecing(self):
1699                 if self.execing:
1700                         self.checkNotifications()
1701
1702         def checkNotifications(self):
1703                 if len(Notifications.notifications):
1704                         n = Notifications.notifications[0]
1705
1706                         Notifications.notifications = Notifications.notifications[1:]
1707                         cb = n[0]
1708
1709                         if n[3].has_key("onSessionOpenCallback"):
1710                                 n[3]["onSessionOpenCallback"]()
1711                                 del n[3]["onSessionOpenCallback"]
1712
1713                         if cb is not None:
1714                                 dlg = self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1715                         else:
1716                                 dlg = self.session.open(n[1], *n[2], **n[3])
1717
1718                         # remember that this notification is currently active
1719                         d = (n[4], dlg)
1720                         Notifications.current_notifications.append(d)
1721                         dlg.onClose.append(boundFunction(self.__notificationClosed, d))
1722
1723         def __notificationClosed(self, d):
1724                 Notifications.current_notifications.remove(d)
1725
1726 class InfoBarServiceNotifications:
1727         def __init__(self):
1728                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1729                         {
1730                                 iPlayableService.evEnd: self.serviceHasEnded
1731                         })
1732
1733         def serviceHasEnded(self):
1734                 print "service end!"
1735
1736                 try:
1737                         self.setSeekState(self.SEEK_STATE_PLAY)
1738                 except:
1739                         pass
1740
1741 class InfoBarCueSheetSupport:
1742         CUT_TYPE_IN = 0
1743         CUT_TYPE_OUT = 1
1744         CUT_TYPE_MARK = 2
1745         CUT_TYPE_LAST = 3
1746
1747         ENABLE_RESUME_SUPPORT = False
1748
1749         def __init__(self, actionmap = "InfobarCueSheetActions"):
1750                 self["CueSheetActions"] = HelpableActionMap(self, actionmap, 
1751                         {
1752                                 "jumpPreviousMark": (self.jumpPreviousMark, _("jump to previous marked position")),
1753                                 "jumpNextMark": (self.jumpNextMark, _("jump to next marked position")),
1754                                 "toggleMark": (self.toggleMark, _("toggle a cut mark at the current position"))
1755                         }, prio=1) 
1756
1757                 self.cut_list = [ ]
1758                 self.is_closing = False
1759                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1760                         {
1761                                 iPlayableService.evStart: self.__serviceStarted,
1762                         })
1763
1764         def __serviceStarted(self):
1765                 if self.is_closing:
1766                         return
1767                 print "new service started! trying to download cuts!"
1768                 self.downloadCuesheet()
1769
1770                 if self.ENABLE_RESUME_SUPPORT:
1771                         last = None
1772
1773                         for (pts, what) in self.cut_list:
1774                                 if what == self.CUT_TYPE_LAST:
1775                                         last = pts
1776
1777                         if last is not None:
1778                                 self.resume_point = last
1779                                 Notifications.AddNotificationWithCallback(self.playLastCB, MessageBox, _("Do you want to resume this playback?"), timeout=10)
1780
1781         def playLastCB(self, answer):
1782                 if answer == True:
1783                         seekable = self.__getSeekable()
1784                         if seekable is not None:
1785                                 seekable.seekTo(self.resume_point)
1786                 self.hideAfterResume()
1787         
1788         def hideAfterResume(self):
1789                 if isinstance(self, InfoBarShowHide):
1790                         self.hide()
1791
1792         def __getSeekable(self):
1793                 service = self.session.nav.getCurrentService()
1794                 if service is None:
1795                         return None
1796                 return service.seek()
1797
1798         def cueGetCurrentPosition(self):
1799                 seek = self.__getSeekable()
1800                 if seek is None:
1801                         return None
1802                 r = seek.getPlayPosition()
1803                 if r[0]:
1804                         return None
1805                 return long(r[1])
1806
1807         def jumpPreviousNextMark(self, cmp, alternative=None):
1808                 current_pos = self.cueGetCurrentPosition()
1809                 if current_pos is None:
1810                         return
1811                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1812                 if mark is not None:
1813                         pts = mark[0]
1814                 elif alternative is not None:
1815                         pts = alternative
1816                 else:
1817                         return
1818
1819                 seekable = self.__getSeekable()
1820                 if seekable is not None:
1821                         seekable.seekTo(pts)
1822
1823         def jumpPreviousMark(self):
1824                 # we add 2 seconds, so if the play position is <2s after
1825                 # the mark, the mark before will be used
1826                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1827
1828         def jumpNextMark(self):
1829                 self.jumpPreviousNextMark(lambda x: x)
1830
1831         def getNearestCutPoint(self, pts, cmp=abs):
1832                 # can be optimized
1833                 nearest = None
1834                 for cp in self.cut_list:
1835                         diff = cmp(cp[0] - pts)
1836                         if cp[1] == self.CUT_TYPE_MARK and diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1837                                 nearest = cp
1838                 return nearest
1839
1840         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1841                 current_pos = self.cueGetCurrentPosition()
1842                 if current_pos is None:
1843                         print "not seekable"
1844                         return
1845
1846                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1847
1848                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1849                         if onlyreturn:
1850                                 return nearest_cutpoint
1851                         if not onlyadd:
1852                                 self.removeMark(nearest_cutpoint)
1853                 elif not onlyremove and not onlyreturn:
1854                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1855
1856                 if onlyreturn:
1857                         return None
1858
1859         def addMark(self, point):
1860                 insort(self.cut_list, point)
1861                 self.uploadCuesheet()
1862                 self.showAfterCuesheetOperation()
1863
1864         def removeMark(self, point):
1865                 self.cut_list.remove(point)
1866                 self.uploadCuesheet()
1867                 self.showAfterCuesheetOperation()
1868
1869         def showAfterCuesheetOperation(self):
1870                 if isinstance(self, InfoBarShowHide):
1871                         self.doShow()
1872
1873         def __getCuesheet(self):
1874                 service = self.session.nav.getCurrentService()
1875                 if service is None:
1876                         return None
1877                 return service.cueSheet()
1878
1879         def uploadCuesheet(self):
1880                 cue = self.__getCuesheet()
1881
1882                 if cue is None:
1883                         print "upload failed, no cuesheet interface"
1884                         return
1885                 cue.setCutList(self.cut_list)
1886
1887         def downloadCuesheet(self):
1888                 cue = self.__getCuesheet()
1889
1890                 if cue is None:
1891                         print "download failed, no cuesheet interface"
1892                         self.cut_list = [ ]
1893                 else:
1894                         self.cut_list = cue.getCutList()
1895
1896 class InfoBarSummary(Screen):
1897         skin = """
1898         <screen position="0,0" size="132,64">
1899                 <widget source="CurrentTime" render="Label" position="56,46" size="82,18" font="Regular;16" >
1900                         <convert type="ClockToText">WithSeconds</convert>
1901                 </widget>
1902                 <widget source="CurrentService" render="Label" position="6,4" size="120,42" font="Regular;18" >
1903                         <convert type="ServiceName">Name</convert>
1904                 </widget>
1905         </screen>"""
1906
1907         def __init__(self, session, parent):
1908                 Screen.__init__(self, session)
1909                 self["CurrentService"] = CurrentService(self.session.nav)
1910                 self["CurrentTime"] = Clock()
1911
1912 class InfoBarSummarySupport:
1913         def __init__(self):
1914                 pass
1915
1916         def createSummary(self):
1917                 return InfoBarSummary
1918
1919 class InfoBarTeletextPlugin:
1920         def __init__(self):
1921                 self.teletext_plugin = None
1922
1923                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1924                         self.teletext_plugin = p
1925
1926                 if self.teletext_plugin is not None:
1927                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1928                                 {
1929                                         "startTeletext": (self.startTeletext, _("View teletext..."))
1930                                 })
1931                 else:
1932                         print "no teletext plugin found!"
1933
1934         def startTeletext(self):
1935                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())
1936
1937 class InfoBarSubtitleSupport(object):
1938         def __init__(self):
1939                 object.__init__(self)
1940                 self.subtitle_window = self.session.instantiateDialog(SubtitleDisplay)
1941                 self.__subtitles_enabled = False
1942
1943                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1944                         {
1945                                 iPlayableService.evEnd: self.__serviceStopped,
1946                                 iPlayableService.evUpdatedInfo: self.__updatedInfo
1947                         })
1948                 self.cached_subtitle_checked = False
1949                 self.__selected_subtitle = None
1950
1951         def __serviceStopped(self):
1952                 self.subtitle_window.hide()
1953                 self.__subtitles_enabled = False
1954                 self.cached_subtitle_checked = False
1955
1956         def __updatedInfo(self):
1957                 if not self.cached_subtitle_checked:
1958                         subtitle = self.getCurrentServiceSubtitle()
1959                         self.cached_subtitle_checked = True
1960                         self.__selected_subtitle = subtitle and subtitle.getCachedSubtitle()
1961                         if self.__selected_subtitle:
1962                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1963                                 self.subtitle_window.show()
1964                                 self.__subtitles_enabled = True
1965
1966         def getCurrentServiceSubtitle(self):
1967                 service = self.session.nav.getCurrentService()
1968                 return service and service.subtitle()
1969
1970         def setSubtitlesEnable(self, enable=True):
1971                 subtitle = self.getCurrentServiceSubtitle()
1972                 if enable and self.__selected_subtitle is not None:
1973                         if subtitle and not self.__subtitles_enabled:
1974                                 subtitle.enableSubtitles(self.subtitle_window.instance, self.selected_subtitle)
1975                                 self.subtitle_window.show()
1976                                 self.__subtitles_enabled = True
1977                 else:
1978                         if subtitle:
1979                                 subtitle.disableSubtitles(self.subtitle_window.instance)
1980                         self.__subtitles_enabled = False
1981                         self.subtitle_window.hide()
1982
1983         def setSelectedSubtitle(self, subtitle):
1984                 self.__selected_subtitle = subtitle
1985
1986         subtitles_enabled = property(lambda self: self.__subtitles_enabled, setSubtitlesEnable)
1987         selected_subtitle = property(lambda self: self.__selected_subtitle, setSelectedSubtitle)
1988
1989 class InfoBarServiceErrorPopupSupport:
1990         def __init__(self):
1991                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1992                         {
1993                                 iPlayableService.evTuneFailed: self.__tuneFailed,
1994                                 iPlayableService.evStart: self.__serviceStarted
1995                         })
1996                 self.__serviceStarted()
1997
1998         def __serviceStarted(self):
1999                 self.last_error = None
2000                 Notifications.RemovePopup(id = "ZapError")
2001
2002         def __tuneFailed(self):
2003                 service = self.session.nav.getCurrentService()
2004                 info = service and service.info()
2005                 error = info and info.getInfo(iServiceInformation.sDVBState)
2006
2007                 if error == self.last_error:
2008                         error = None
2009                 else:
2010                         self.last_error = error
2011
2012                 errors = {
2013                         eDVBServicePMTHandler.eventNoResources: _("No free tuner!"),
2014                         eDVBServicePMTHandler.eventTuneFailed: _("Tune failed!"),
2015                         eDVBServicePMTHandler.eventNoPAT: _("No data on transponder!\n(Timeout reading PAT)"),
2016                         eDVBServicePMTHandler.eventNoPATEntry: _("Service not found!\n(SID not found in PAT)"),
2017                         eDVBServicePMTHandler.eventNoPMT: _("Service invalid!\n(Timeout reading PMT)"),
2018                         eDVBServicePMTHandler.eventNewProgramInfo: None,
2019                         eDVBServicePMTHandler.eventTuned: None,
2020                         eDVBServicePMTHandler.eventSOF: None,
2021                         eDVBServicePMTHandler.eventEOF: None
2022                 }
2023
2024                 error = errors.get(error) #this returns None when the key not exist in the dict
2025
2026                 if error is not None:
2027                         Notifications.AddPopup(text = error, type = MessageBox.TYPE_ERROR, timeout = 5, id = "ZapError")
2028                 else:
2029                         Notifications.RemovePopup(id = "ZapError")