replace subservice selection screen with ChoiceBox screen
[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.Clock import Clock
7 from Components.EventInfo import EventInfo, EventInfoProgress
8 from Components.Harddisk import harddiskmanager
9 from Components.Input import Input
10 from Components.Label import *
11 from Components.Pixmap import Pixmap, PixmapConditional
12 from Components.PluginComponent import plugins
13 from Components.ProgressBar import *
14 from Components.ServiceEventTracker import ServiceEventTracker
15 from Components.ServiceName import ServiceName
16 from Components.config import config, configElement, ConfigSubsection, configSequence, configElementBoolean
17 from Components.config import configfile, configsequencearg
18 from Components.TimerList import TimerEntryComponent
19 from Components.TunerInfo import TunerInfo
20
21 from EpgSelection import EPGSelection
22 from Plugins.Plugin import PluginDescriptor
23
24 from Screen import Screen
25 from Screens.ChoiceBox import ChoiceBox
26 from Screens.Dish import Dish
27 from Screens.EventView import EventViewEPGSelect, EventViewSimple
28 from Screens.InputBox import InputBox
29 from Screens.MessageBox import MessageBox
30 from Screens.MinuteInput import MinuteInput
31 from Screens.TimerSelection import TimerSelection
32 from Screens.PictureInPicture import PictureInPicture
33 from ServiceReference import ServiceReference
34
35 from Tools import Notifications
36 from Tools.Directories import *
37
38 #from enigma import eTimer, eDVBVolumecontrol, quitMainloop
39 from enigma import *
40
41 import time
42 import os
43 import bisect
44
45 from Components.config import config, currentConfigSelectionElement
46
47 # hack alert!
48 from Menu import MainMenu, mdom
49
50 class InfoBarDish:
51         def __init__(self):
52                 self.dishDialog = self.session.instantiateDialog(Dish)
53                 self.onLayoutFinish.append(self.dishDialog.show)
54
55 class InfoBarShowHide:
56         """ InfoBar show/hide control, accepts toggleShow and hide actions, might start
57         fancy animations. """
58         STATE_HIDDEN = 0
59         STATE_HIDING = 1
60         STATE_SHOWING = 2
61         STATE_SHOWN = 3
62         
63         def __init__(self):
64                 self["ShowHideActions"] = ActionMap( ["InfobarShowHideActions"] ,
65                         {
66                                 "toggleShow": self.toggleShow,
67                                 "hide": self.hide,
68                         })
69
70                 self.__state = self.STATE_SHOWN
71                 self.__locked = 0
72                 
73                 self.onExecBegin.append(self.show)
74                 
75                 self.hideTimer = eTimer()
76                 self.hideTimer.timeout.get().append(self.doTimerHide)
77                 self.hideTimer.start(5000, True)
78                 
79                 self.onShow.append(self.__onShow)
80                 self.onHide.append(self.__onHide)
81
82         def __onShow(self):
83                 self.__state = self.STATE_SHOWN
84                 self.startHideTimer()
85         
86         def startHideTimer(self):
87                 if self.__state == self.STATE_SHOWN and not self.__locked:
88                         self.hideTimer.start(5000, True)
89
90         def __onHide(self):
91                 self.__state = self.STATE_HIDDEN
92
93         def doShow(self):
94                 self.show()
95                 self.startHideTimer()
96
97         def doTimerHide(self):
98                 self.hideTimer.stop()
99                 if self.__state == self.STATE_SHOWN:
100                         self.hide()
101
102         def toggleShow(self):
103                 if self.__state == self.STATE_SHOWN:
104                         self.hide()
105                         self.hideTimer.stop()
106                 elif self.__state == self.STATE_HIDDEN:
107                         self.show()
108
109         def lockShow(self):
110                 self.__locked = self.__locked + 1
111                 if self.execing:
112                         self.show()
113                         self.hideTimer.stop()
114         
115         def unlockShow(self):
116                 self.__locked = self.__locked - 1
117                 if self.execing:
118                         self.startHideTimer()
119
120 #       def startShow(self):
121 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 600), ePoint(0, 380), 100)
122 #               self.__state = self.STATE_SHOWN
123 #       
124 #       def startHide(self):
125 #               self.instance.m_animation.startMoveAnimation(ePoint(0, 380), ePoint(0, 600), 100)
126 #               self.__state = self.STATE_HIDDEN
127
128 class NumberZap(Screen):
129         def quit(self):
130                 self.Timer.stop()
131                 self.close(0)
132
133         def keyOK(self):
134                 self.Timer.stop()
135                 self.close(int(self["number"].getText()))
136
137         def keyNumberGlobal(self, number):
138                 self.Timer.start(3000, True)            #reset timer
139                 self.field = self.field + str(number)
140                 self["number"].setText(self.field)
141                 if len(self.field) >= 4:
142                         self.keyOK()
143
144         def __init__(self, session, number):
145                 Screen.__init__(self, session)
146                 self.field = str(number)
147
148                 self["channel"] = Label(_("Channel:"))
149
150                 self["number"] = Label(self.field)
151
152                 self["actions"] = NumberActionMap( [ "SetupActions" ], 
153                         {
154                                 "cancel": self.quit,
155                                 "ok": self.keyOK,
156                                 "1": self.keyNumberGlobal,
157                                 "2": self.keyNumberGlobal,
158                                 "3": self.keyNumberGlobal,
159                                 "4": self.keyNumberGlobal,
160                                 "5": self.keyNumberGlobal,
161                                 "6": self.keyNumberGlobal,
162                                 "7": self.keyNumberGlobal,
163                                 "8": self.keyNumberGlobal,
164                                 "9": self.keyNumberGlobal,
165                                 "0": self.keyNumberGlobal
166                         })
167
168                 self.Timer = eTimer()
169                 self.Timer.timeout.get().append(self.keyOK)
170                 self.Timer.start(3000, True)
171
172 class InfoBarNumberZap:
173         """ Handles an initial number for NumberZapping """
174         def __init__(self):
175                 self["NumberActions"] = NumberActionMap( [ "NumberActions"],
176                         {
177                                 "1": self.keyNumberGlobal,
178                                 "2": self.keyNumberGlobal,
179                                 "3": self.keyNumberGlobal,
180                                 "4": self.keyNumberGlobal,
181                                 "5": self.keyNumberGlobal,
182                                 "6": self.keyNumberGlobal,
183                                 "7": self.keyNumberGlobal,
184                                 "8": self.keyNumberGlobal,
185                                 "9": self.keyNumberGlobal,
186                                 "0": self.keyNumberGlobal,
187                         })
188
189         def keyNumberGlobal(self, number):
190 #               print "You pressed number " + str(number)
191                 if number == 0:
192                         self.servicelist.recallPrevService()
193                         self.doShow()
194                 else:
195                         self.session.openWithCallback(self.numberEntered, NumberZap, number)
196
197         def numberEntered(self, retval):
198 #               print self.servicelist
199                 if retval > 0:
200                         self.zapToNumber(retval)
201
202         def searchNumberHelper(self, serviceHandler, num, bouquet):
203                 servicelist = serviceHandler.list(bouquet)
204                 if not servicelist is None:
205                         while num:
206                                 serviceIterator = servicelist.getNext()
207                                 if not serviceIterator.valid(): #check end of list
208                                         break
209                                 if serviceIterator.flags: #assume normal dvb service have no flags set
210                                         continue
211                                 num -= 1;
212                         if not num: #found service with searched number ?
213                                 return serviceIterator, 0
214                 return None, num
215
216         def zapToNumber(self, number):
217                 bouquet = self.servicelist.bouquet_root
218                 service = None
219                 serviceHandler = eServiceCenter.getInstance()
220                 if bouquet.toString().find('FROM BOUQUET "bouquets.') == -1: #FIXME HACK
221                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
222                 else:
223                         bouquetlist = serviceHandler.list(bouquet)
224                         if not bouquetlist is None:
225                                 while number:
226                                         bouquet = self.servicelist.appendDVBTypes(bouquetlist.getNext())
227                                         if not bouquet.valid(): #check end of list
228                                                 break
229                                         if (bouquet.flags & eServiceReference.flagDirectory) != eServiceReference.flagDirectory:
230                                                 continue
231                                         service, number = self.searchNumberHelper(serviceHandler, number, bouquet)
232                 if not service is None:
233                         if self.servicelist.getRoot() != bouquet: #already in correct bouquet?
234                                 self.servicelist.clearPath()
235                                 if self.servicelist.bouquet_root != bouquet:
236                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
237                                 self.servicelist.enterPath(bouquet)
238                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
239                         self.servicelist.zap()
240
241 config.misc.initialchannelselection = configElementBoolean("config.misc.initialchannelselection", 1);
242
243 class InfoBarChannelSelection:
244         """ ChannelSelection - handles the channelSelection dialog and the initial 
245         channelChange actions which open the channelSelection dialog """
246         def __init__(self):
247                 #instantiate forever
248                 self.servicelist = self.session.instantiateDialog(ChannelSelection)
249                 
250                 if config.misc.initialchannelselection.value == 1:
251                         self.onShown.append(self.firstRun)
252
253                 self["ChannelSelectActions"] = HelpableActionMap(self, "InfobarChannelSelection",
254                         {
255                                 "switchChannelUp": self.switchChannelUp,
256                                 "switchChannelDown": self.switchChannelDown,
257                                 "zapUp": (self.zapUp, _("previous channel")),
258                                 "zapDown": (self.zapDown, _("next channel")),
259                                 "historyBack": (self.historyBack, _("previous channel in history")),
260                                 "historyNext": (self.historyNext, _("next channel in history"))
261                         })
262
263         def firstRun(self):
264                 self.onShown.remove(self.firstRun)
265                 config.misc.initialchannelselection.value = 0
266                 config.misc.initialchannelselection.save()
267                 self.switchChannelDown()
268                 
269         def historyBack(self):
270                 self.servicelist.historyBack()
271
272         def historyNext(self):
273                 self.servicelist.historyNext()
274
275         def switchChannelUp(self):
276                 self.servicelist.moveUp()
277                 self.session.execDialog(self.servicelist)
278
279         def switchChannelDown(self):
280                 self.servicelist.moveDown()
281                 self.session.execDialog(self.servicelist)
282
283         def zapUp(self):
284                 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes":
285                         if self.servicelist.inBouquet() and self.servicelist.atBegin():
286                                 self.servicelist.prevBouquet()
287                 self.servicelist.moveUp()
288                 self.servicelist.zap()
289                 self.doShow()
290
291         def zapDown(self):
292                 if currentConfigSelectionElement(config.usage.quickzap_bouquet_change) == "yes" and self.servicelist.inBouquet() and self.servicelist.atEnd():
293                         self.servicelist.nextBouquet()
294                 else:
295                         self.servicelist.moveDown()
296                 self.servicelist.zap()
297                 self.doShow()
298
299 class InfoBarMenu:
300         """ Handles a menu action, to open the (main) menu """
301         def __init__(self):
302                 self["MenuActions"] = HelpableActionMap(self, "InfobarMenuActions", 
303                         {
304                                 "mainMenu": (self.mainMenu, "Enter main menu..."),
305                         })
306
307         def mainMenu(self):
308                 print "loading mainmenu XML..."
309                 menu = mdom.childNodes[0]
310                 assert menu.tagName == "menu", "root element in menu must be 'menu'!"
311                 self.session.open(MainMenu, menu, menu.childNodes)
312
313 class InfoBarSimpleEventView:
314         """ Opens the Eventview for now/next """
315         def __init__(self):
316                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions",
317                         {
318                                 "showEventInfo": (self.openEventView, _("show event details")),
319                         })
320
321         def openEventView(self):
322                 self.epglist = [ ]
323                 service = self.session.nav.getCurrentService()
324                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
325                 info = service.info()
326                 ptr=info.getEvent(0)
327                 if ptr:
328                         self.epglist.append(ptr)
329                 ptr=info.getEvent(1)
330                 if ptr:
331                         self.epglist.append(ptr)
332                 if len(self.epglist) > 0:
333                         self.session.open(EventViewSimple, self.epglist[0], ServiceReference(ref), self.eventViewCallback)
334
335         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
336                 if len(self.epglist) > 1:
337                         tmp = self.epglist[0]
338                         self.epglist[0]=self.epglist[1]
339                         self.epglist[1]=tmp
340                         setEvent(self.epglist[0])
341
342 class InfoBarEPG:
343         """ EPG - Opens an EPG list when the showEPGList action fires """
344         def __init__(self):
345                 self.dlg_stack = [ ]
346                 self.bouquetSel = None
347                 self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
348                         {
349                                 "showEventInfo": (self.openEventView, _("show EPG...")),
350                         })
351
352         def zapToService(self, service):
353                 if not service is None:
354                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
355                                 self.servicelist.clearPath()
356                                 if self.servicelist.bouquet_root != self.epg_bouquet:
357                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
358                                 self.servicelist.enterPath(self.epg_bouquet)
359                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
360                         self.servicelist.zap()
361
362         def getBouquetServices(self, bouquet):
363                 services = [ ]
364                 servicelist = eServiceCenter.getInstance().list(bouquet)
365                 if not servicelist is None:
366                         while True:
367                                 service = servicelist.getNext()
368                                 if not service.valid(): #check if end of list
369                                         break
370                                 if service.flags: #ignore non playable services
371                                         continue
372                                 services.append(ServiceReference(service))
373                 return services
374
375         def openBouquetEPG(self, bouquet, withCallback=True):
376                 services = self.getBouquetServices(bouquet)
377                 if len(services):
378                         self.epg_bouquet = bouquet
379                         if withCallback:
380                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService, None, self.changeBouquetCB))
381                         else:
382                                 self.session.open(EPGSelection, services, self.zapToService, None, self.changeBouquetCB)
383
384         def changeBouquetCB(self, direction, epg):
385                 if self.bouquetSel:
386                         if direction > 0:
387                                 self.bouquetSel.down()
388                         else:
389                                 self.bouquetSel.up()
390                         bouquet = self.bouquetSel.getCurrent()
391                         services = self.getBouquetServices(bouquet)
392                         if len(services):
393                                 self.epg_bouquet = bouquet
394                                 epg.setServices(services)
395
396         def closed(self, ret=False):
397                 closedScreen = self.dlg_stack.pop()
398                 if self.bouquetSel and closedScreen == self.bouquetSel:
399                         self.bouquetSel = None
400                 if ret:
401                         dlgs=len(self.dlg_stack)
402                         assert dlgs>0
403                         self.dlg_stack[dlgs-1].close(dlgs > 1)
404
405         def openMultiServiceEPG(self, withCallback=True):
406                 bouquets = self.servicelist.getBouquetList()
407                 if bouquets is None:
408                         cnt = 0
409                 else:
410                         cnt = len(bouquets)
411                 if cnt > 1: # show bouquet list
412                         if withCallback:
413                                 self.bouquetSel = self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
414                                 self.dlg_stack.append(self.bouquetSel)
415                         else:
416                                 self.bouquetSel = self.session.open(BouquetSelector, bouquets, self.openBouquetEPG, enableWrapAround=True)
417                 elif cnt == 1: 
418                         self.openBouquetEPG(bouquets[0][1], withCallback)
419
420         def openSingleServiceEPG(self):
421                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
422                 self.session.open(EPGSelection, ref)
423
424         def openSimilarList(self, eventid, refstr):
425                 self.session.open(EPGSelection, refstr, None, eventid)
426
427         def openEventView(self):
428                 self.epglist = [ ]
429                 service = self.session.nav.getCurrentService()
430                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
431                 info = service.info()
432                 ptr=info.getEvent(0)
433                 if ptr:
434                         self.epglist.append(ptr)
435                 ptr=info.getEvent(1)
436                 if ptr:
437                         self.epglist.append(ptr)
438                 if len(self.epglist) == 0:
439                         epg = eEPGCache.getInstance()
440                         ptr = epg.lookupEventTime(ref, -1)
441                         if ptr:
442                                 self.epglist.append(ptr)
443                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
444                                 if ptr:
445                                         self.epglist.append(ptr)
446                 if len(self.epglist) > 0:
447                         self.dlg_stack.append(self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList))
448                 else:
449                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
450                         self.openMultiServiceEPG(False)
451
452         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
453                 if len(self.epglist) > 1:
454                         tmp = self.epglist[0]
455                         self.epglist[0]=self.epglist[1]
456                         self.epglist[1]=tmp
457                         setEvent(self.epglist[0])
458
459 class InfoBarTuner:
460         """provides a snr/agc/ber display"""
461         def __init__(self):
462                 self["snr"] = Label()
463                 self["agc"] = Label()
464                 self["ber"] = Label()
465                 self["snr_percent"] = TunerInfo(TunerInfo.SNR_PERCENTAGE, servicefkt = self.session.nav.getCurrentService)
466                 self["agc_percent"] = TunerInfo(TunerInfo.AGC_PERCENTAGE, servicefkt = self.session.nav.getCurrentService)
467                 self["ber_count"] = TunerInfo(TunerInfo.BER_VALUE, servicefkt = self.session.nav.getCurrentService)
468                 self["snr_progress"] = TunerInfo(TunerInfo.SNR_BAR, servicefkt = self.session.nav.getCurrentService)
469                 self["agc_progress"] = TunerInfo(TunerInfo.AGC_BAR, servicefkt = self.session.nav.getCurrentService)
470                 self["ber_progress"] = TunerInfo(TunerInfo.BER_BAR, servicefkt = self.session.nav.getCurrentService)
471                 self.timer = eTimer()
472                 self.timer.timeout.get().append(self.updateTunerInfo)
473                 self.timer.start(1000)
474
475         def updateTunerInfo(self):
476                 if self.instance.isVisible():
477                         self["snr_percent"].update()
478                         self["agc_percent"].update()
479                         self["ber_count"].update()
480                         self["snr_progress"].update()
481                         self["agc_progress"].update()
482                         self["ber_progress"].update()
483
484 class InfoBarEvent:
485         """provides a current/next event info display"""
486         def __init__(self):
487                 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
488                 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
489                                 
490                 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
491                 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
492
493                 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
494                 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
495
496                 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
497
498 class InfoBarServiceName:
499         def __init__(self):
500                 self["ServiceName"] = ServiceName(self.session.nav)
501
502 class InfoBarSeek:
503         """handles actions like seeking, pause"""
504         
505         # ispause, isff, issm
506         SEEK_STATE_PLAY = (0, 0, 0, ">")
507         SEEK_STATE_PAUSE = (1, 0, 0, "||")
508         SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
509         SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
510         SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
511         SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
512         SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
513         SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
514         
515         SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
516         SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
517         SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
518         SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
519         
520         SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
521         SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
522         SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
523         
524         def __init__(self):
525                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
526                         {
527                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
528                                 iPlayableService.evStart: self.__serviceStarted,
529                                 
530                                 iPlayableService.evEOF: self.__evEOF,
531                                 iPlayableService.evSOF: self.__evSOF,
532                         })
533
534                 class InfoBarSeekActionMap(HelpableActionMap):
535                         def __init__(self, screen, *args, **kwargs):
536                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
537                                 self.screen = screen
538                                 
539                         def action(self, contexts, action):
540                                 if action[:5] == "seek:":
541                                         time = int(action[5:])
542                                         self.screen.seekRelative(time * 90000)
543                                         return 1
544                                 else:
545                                         return HelpableActionMap.action(self, contexts, action)
546
547                 self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions", 
548                         {
549                                 "pauseService": (self.pauseService, "pause"),
550                                 "unPauseService": (self.unPauseService, "continue"),
551                                 
552                                 "seekFwd": (self.seekFwd, "skip forward"),
553                                 "seekFwdDown": self.seekFwdDown,
554                                 "seekFwdUp": self.seekFwdUp,
555                                 "seekBack": (self.seekBack, "skip backward"),
556                                 "seekBackDown": self.seekBackDown,
557                                 "seekBackUp": self.seekBackUp,
558                         }, prio=-1)
559                         # give them a little more priority to win over color buttons
560
561                 self.seekstate = self.SEEK_STATE_PLAY
562                 self.onClose.append(self.delTimer)
563                 
564                 self.fwdtimer = False
565                 self.fwdKeyTimer = eTimer()
566                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
567
568                 self.rwdtimer = False
569                 self.rwdKeyTimer = eTimer()
570                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
571                 
572                 self.onPlayStateChanged = [ ]
573                 
574                 self.lockedBecauseOfSkipping = False
575         
576         def up(self):
577                 pass
578         
579         def down(self):
580                 pass
581         
582         def delTimer(self):
583                 del self.fwdKeyTimer
584                 del self.rwdKeyTimer
585         
586         def getSeek(self):
587                 service = self.session.nav.getCurrentService()
588                 if service is None:
589                         return None
590
591                 seek = service.seek()
592
593                 if seek is None or not seek.isCurrentlySeekable():
594                         return None
595                 
596                 return seek
597         
598         def isSeekable(self):
599                 if self.getSeek() is None:
600                         return False
601                 return True
602
603         def __seekableStatusChanged(self):
604                 print "seekable status changed!"
605                 if not self.isSeekable():
606                         self["SeekActions"].setEnabled(False)
607                         print "not seekable, return to play"
608                         self.setSeekState(self.SEEK_STATE_PLAY)
609                 else:
610                         self["SeekActions"].setEnabled(True)
611                         print "seekable"
612
613         def __serviceStarted(self):
614                 self.seekstate = self.SEEK_STATE_PLAY
615
616         def setSeekState(self, state):
617                 service = self.session.nav.getCurrentService()
618                 
619                 if service is None:
620                         return False
621                 
622                 if not self.isSeekable():
623                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
624                                 state = self.SEEK_STATE_PLAY
625                 
626                 pauseable = service.pause()
627
628                 if pauseable is None:
629                         print "not pauseable."
630                         state = self.SEEK_STATE_PLAY
631                 
632                 oldstate = self.seekstate
633                 self.seekstate = state
634                 
635                 for i in range(3):
636                         if oldstate[i] != self.seekstate[i]:
637                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
638
639                 for c in self.onPlayStateChanged:
640                         c(self.seekstate)
641                 
642                 self.checkSkipShowHideLock()
643
644                 return True
645
646         def pauseService(self):
647                 if self.seekstate == self.SEEK_STATE_PAUSE:
648                         print "pause, but in fact unpause"
649                         self.unPauseService()
650                 else:
651                         if self.seekstate == self.SEEK_STATE_PLAY:
652                                 print "yes, playing."
653                         else:
654                                 print "no", self.seekstate
655                         print "pause"
656                         self.setSeekState(self.SEEK_STATE_PAUSE);
657                 
658         def unPauseService(self):
659                 print "unpause"
660                 self.setSeekState(self.SEEK_STATE_PLAY);
661         
662         def doSeek(self, seektime):
663                 print "doseek", seektime
664                 service = self.session.nav.getCurrentService()
665                 if service is None:
666                         return
667                 
668                 seekable = self.getSeek()
669                 if seekable is None:
670                         return
671                 
672                 seekable.seekTo(90 * seektime)
673
674         def seekFwdDown(self):
675                 print "start fwd timer"
676                 self.fwdtimer = True
677                 self.fwdKeyTimer.start(1000)
678
679         def seekBackDown(self):
680                 print "start rewind timer"
681                 self.rwdtimer = True
682                 self.rwdKeyTimer.start(1000)
683
684         def seekFwdUp(self):
685                 print "seekFwdUp"
686                 if self.fwdtimer:
687                         self.fwdKeyTimer.stop()
688                         self.fwdtimer = False
689                         self.seekFwd()
690
691         def seekFwd(self):
692                 lookup = {
693                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
694                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
695                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
696                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
697                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
698                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
699                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
700                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
701                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
702                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
703                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
704                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
705                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
706                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
707                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
708                         }
709                 self.setSeekState(lookup[self.seekstate])
710         
711         def seekBackUp(self):
712                 print "seekBackUp"
713                 if self.rwdtimer:
714                         self.rwdKeyTimer.stop()
715                         self.rwdtimer = False
716                         self.seekBack()
717                 
718         def seekBack(self):
719                 lookup = {
720                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
721                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
722                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
723                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
724                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
725                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
726                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
727                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
728                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
729                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
730                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
731                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
732                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
733                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
734                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
735                         }
736                 self.setSeekState(lookup[self.seekstate])
737                 
738                 if self.seekstate == self.SEEK_STATE_PAUSE:
739                         seekable = self.getSeek()
740                         if seekable is not None:
741                                 seekable.seekRelative(-1, 3)
742
743         def fwdTimerFire(self):
744                 print "Display seek fwd"
745                 self.fwdKeyTimer.stop()
746                 self.fwdtimer = False
747                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
748                 
749         def fwdSeekTo(self, minutes):
750                 print "Seek", minutes, "minutes forward"
751                 if minutes != 0:
752                         seekable = self.getSeek()
753                         if seekable is not None:
754                                 seekable.seekRelative(1, minutes * 60 * 90000)
755         
756         def rwdTimerFire(self):
757                 print "rwdTimerFire"
758                 self.rwdKeyTimer.stop()
759                 self.rwdtimer = False
760                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
761         
762         def rwdSeekTo(self, minutes):
763                 print "rwdSeekTo"
764                 self.fwdSeekTo(0 - minutes)
765         
766         def checkSkipShowHideLock(self):
767                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
768                 
769                 if self.lockedBecauseOfSkipping and not wantlock:
770                         self.unlockShow()
771                         self.lockedBecauseOfSkipping = False
772                 
773                 if wantlock and not self.lockedBecauseOfSkipping:
774                         self.lockShow()
775                         self.lockedBecauseOfSkipping = True
776
777         def __evEOF(self):
778                 if self.seekstate != self.SEEK_STATE_PLAY:
779                         self.setSeekState(self.SEEK_STATE_PAUSE)
780                         # HACK
781                         #self.getSeek().seekRelative(1, -90000)
782                         self.setSeekState(self.SEEK_STATE_PLAY)
783                 else:
784                         self.setSeekState(self.SEEK_STATE_PAUSE)
785         
786         def __evSOF(self):
787                 self.setSeekState(self.SEEK_STATE_PLAY)
788                 self.doSeek(0)
789
790         def seekRelative(self, diff):
791                 seekable = self.getSeek()
792                 if seekable is not None:
793                         seekable.seekRelative(1, diff)
794
795 from Screens.PVRState import PVRState, TimeshiftState
796
797 class InfoBarPVRState:
798         def __init__(self, screen=PVRState):
799                 self.onPlayStateChanged.append(self.__playStateChanged)
800                 self.pvrStateDialog = self.session.instantiateDialog(screen)
801                 self.onShow.append(self.__mayShow)
802                 self.onHide.append(self.pvrStateDialog.hide)
803         
804         def __mayShow(self):
805                 if self.seekstate != self.SEEK_STATE_PLAY and self.execing:
806                         self.pvrStateDialog.show()
807
808         def __playStateChanged(self, state):
809                 playstateString = state[3]
810                 self.pvrStateDialog["state"].setText(playstateString)
811                 self.__mayShow()
812
813 class InfoBarTimeshiftState(InfoBarPVRState):
814         def __init__(self):
815                 InfoBarPVRState.__init__(self, screen=TimeshiftState)
816
817
818 class InfoBarShowMovies:
819
820         # i don't really like this class. 
821         # it calls a not further specified "movie list" on up/down/movieList,
822         # so this is not more than an action map
823         def __init__(self):
824                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
825                         {
826                                 "movieList": (self.showMovies, "movie list"),
827                                 "up": (self.showMovies, "movie list"),
828                                 "down": (self.showMovies, "movie list")
829                         })
830
831 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
832
833 # Hrmf.
834 #
835 # Timeshift works the following way:
836 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
837 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
838 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
839 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
840 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
841 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
842 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
843 #
844
845 # in other words:
846 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
847 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
848 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
849 # - the user can now PVR around
850 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
851 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
852 # after!
853 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
854 # - if the user rewinds, or press pause, timeshift will be activated again
855
856 # note that a timeshift can be enabled ("recording") and
857 # activated (currently time-shifting).
858
859 class InfoBarTimeshift:
860         def __init__(self):
861                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
862                         {
863                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
864                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
865                         }, prio=1)
866                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
867                         {
868                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
869                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
870                         }, prio=-1) # priority over record
871
872                 self.timeshift_enabled = 0
873                 self.timeshift_state = 0
874                 self.ts_pause_timer = eTimer()
875                 self.ts_pause_timer.timeout.get().append(self.pauseService)
876
877                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
878                         {
879                                 iPlayableService.evStart: self.__serviceStarted,
880                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
881                         })
882         
883         def getTimeshift(self):
884                 service = self.session.nav.getCurrentService()
885                 return service and service.timeshift()
886
887         def startTimeshift(self):
888                 print "enable timeshift"
889                 ts = self.getTimeshift()
890                 if ts is None:
891                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
892                         print "no ts interface"
893                         return
894                 
895                 if self.timeshift_enabled:
896                         print "hu, timeshift already enabled?"
897                 else:
898                         if not ts.startTimeshift():
899                                 import time
900                                 self.timeshift_enabled = 1
901                                 self.pvrStateDialog["timeshift"].setRelative(time.time())
902                                 
903                                 # PAUSE.
904                                 self.setSeekState(self.SEEK_STATE_PAUSE)
905                                 
906                                 # enable the "TimeshiftEnableActions", which will override
907                                 # the startTimeshift actions
908                                 self.__seekableStatusChanged()
909                         else:
910                                 print "timeshift failed"
911
912         def stopTimeshift(self):
913                 if not self.timeshift_enabled:
914                         return
915                 print "disable timeshift"
916                 ts = self.getTimeshift()
917                 if ts is None:
918                         return
919                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
920
921         def stopTimeshiftConfirmed(self, confirmed):
922                 if not confirmed:
923                         return
924
925                 ts = self.getTimeshift()
926                 if ts is None:
927                         return
928
929                 ts.stopTimeshift()
930                 self.timeshift_enabled = 0
931
932                 # disable actions
933                 self.__seekableStatusChanged()
934         
935         # activates timeshift, and seeks to (almost) the end
936         def activateTimeshiftEnd(self):
937                 ts = self.getTimeshift()
938                 
939                 if ts is None:
940                         return
941                 
942                 if ts.isTimeshiftActive():
943                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
944                         self.pauseService()
945                 else:
946                         self.setSeekState(self.SEEK_STATE_PLAY)
947                         ts.activateTimeshift()
948                         self.seekRelative(0)
949         
950         # same as activateTimeshiftEnd, but pauses afterwards.
951         def activateTimeshiftEndAndPause(self):
952                 state = self.seekstate
953                 self.activateTimeshiftEnd()
954                 
955                 # well, this is "andPause", but it could be pressed from pause,
956                 # when pausing on the (fake-)"live" picture, so an un-pause
957                 # is perfectly ok.
958                 
959                 print "now, pauseService"
960                 if state == self.SEEK_STATE_PLAY:
961                         print "is PLAYING, start pause timer"
962                         self.ts_pause_timer.start(200, 1)
963                 else:
964                         print "unpause"
965                         self.unPauseService()
966         
967         def __seekableStatusChanged(self):
968                 enabled = False
969                 
970                 print "self.isSeekable", self.isSeekable()
971                 print "self.timeshift_enabled", self.timeshift_enabled
972                 
973                 # when this service is not seekable, but timeshift
974                 # is enabled, this means we can activate
975                 # the timeshift
976                 if not self.isSeekable() and self.timeshift_enabled:
977                         enabled = True
978
979                 print "timeshift activate:", enabled
980                 self["TimeshiftActivateActions"].setEnabled(enabled)
981
982         def __serviceStarted(self):
983                 self.timeshift_enabled = False
984                 self.__seekableStatusChanged()
985
986 class InfoBarExtensions:
987         def __init__(self):
988                 self.pipshown = False
989                 
990                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
991                         {
992                                 "extensions": (self.extensions, "Extensions..."),
993                         })
994                         
995         def extensions(self):
996                 list = []
997                 if self.pipshown == False:
998                         list.append((_("Activate Picture in Picture"), "pipon"))
999                 elif self.pipshown == True:
1000                         list.append((_("Disable Picture in Picture"), "pipoff"))
1001                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list)
1002
1003         def extensionCallback(self, answer):
1004                 if answer is not None:
1005                         if answer[1] == "pipon":
1006                                 self.session.nav.stopService()
1007                                 self.pip = self.session.instantiateDialog(PictureInPicture)
1008                                 #self.pip.show()
1009                                 
1010                                 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1011                                 self.pipservice = eServiceCenter.getInstance().play(newservice)
1012                                 if self.pipservice and not self.pipservice.setTarget(1):
1013                                         self.pipservice.start()
1014                                         self.pipshown = True
1015                                 else:
1016                                         self.pipservice = None
1017                                         del self.pip
1018         
1019                         elif answer[1] == "pipoff":
1020                                 #self.pip.hide()
1021                                 self.pipservice = None
1022                                 del self.pip
1023                                 self.pipshown = False
1024
1025 from RecordTimer import parseEvent
1026
1027 class InfoBarInstantRecord:
1028         """Instant Record - handles the instantRecord action in order to 
1029         start/stop instant records"""
1030         def __init__(self):
1031                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1032                         {
1033                                 "instantRecord": (self.instantRecord, "Instant Record..."),
1034                         })
1035                 self.recording = []
1036                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1037                 self["BlinkingPoint"].hide()
1038                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1039
1040         def stopCurrentRecording(self, entry = -1):     
1041                 if entry is not None and entry != -1:
1042                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1043                         self.recording.remove(self.recording[entry])
1044                         
1045         def startInstantRecording(self, limitEvent = False):
1046                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1047                 
1048                 # try to get event info
1049                 event = None
1050                 try:
1051                         service = self.session.nav.getCurrentService()
1052                         epg = eEPGCache.getInstance()
1053                         event = epg.lookupEventTime(serviceref, -1, 0)
1054                         if event is None:
1055                                 info = service.info()
1056                                 ev = info.getEvent(0)
1057                                 event = ev
1058                 except:
1059                         pass
1060
1061                 begin = time.time()
1062                 end = time.time() + 3600 * 10
1063                 name = "instant record"
1064                 description = ""
1065                 eventid = None
1066                 
1067                 if event is not None:
1068                         curEvent = parseEvent(event)
1069                         name = curEvent[2]
1070                         description = curEvent[3]
1071                         eventid = curEvent[4]
1072                         if limitEvent:
1073                                 end = curEvent[1]
1074                 else:
1075                         if limitEvent:
1076                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1077                                 
1078                 data = (begin, end, name, description, eventid)
1079                 
1080                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1081                 recording.dontSave = True
1082                 self.recording.append(recording)
1083                 
1084                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1085                 
1086         def isInstantRecordRunning(self):
1087                 print "self.recording:", self.recording
1088                 if len(self.recording) > 0:
1089                         for x in self.recording:
1090                                 if x.isRunning():
1091                                         return True
1092                 return False
1093
1094         def recordQuestionCallback(self, answer):
1095                 print "pre:\n", self.recording
1096                 
1097                 if answer is None or answer[1] == "no":
1098                         return
1099                 list = []
1100                 recording = self.recording[:]
1101                 for x in recording:
1102                         if not x in self.session.nav.RecordTimer.timer_list:
1103                                 self.recording.remove(x)
1104                         elif x.dontSave and x.isRunning():
1105                                 list.append(TimerEntryComponent(x, False))              
1106
1107                 if answer[1] == "changeduration":
1108                         if len(self.recording) == 1:
1109                                 self.changeDuration(0)
1110                         else:
1111                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1112                 elif answer[1] == "stop":
1113                         if len(self.recording) == 1:
1114                                 self.stopCurrentRecording(0)
1115                         else:
1116                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1117                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1118                         limitEvent = False
1119                         if answer[1] == "event":
1120                                 limitEvent = True
1121                         if answer[1] == "manualduration":
1122                                 self.selectedEntry = len(self.recording)
1123                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1124                         self.startInstantRecording(limitEvent = limitEvent)
1125                         
1126                 print "after:\n", self.recording
1127
1128         def changeDuration(self, entry):
1129                 if entry is not None:
1130                         self.selectedEntry = entry
1131                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1132
1133         def inputCallback(self, value):
1134                 if value is not None:
1135                         print "stopping recording after", int(value), "minutes."
1136                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1137                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1138
1139         def instantRecord(self):
1140                 try:
1141                         stat = os.stat(resolveFilename(SCOPE_HDD))
1142                 except:
1143                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1144                         return
1145         
1146                 if self.isInstantRecordRunning():
1147                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("A recording is currently running.\nWhat do you want to do?"), list=[(_("stop recording"), "stop"), (_("change recording (duration)"), "changeduration"), (_("add recording (indefinitely)"), "indefinitely"), (_("add recording (stop after current event)"), "event"), (_("add recording (enter recording duration)"), "manualduration"), (_("do nothing"), "no")])
1148                 else:
1149                         self.session.openWithCallback(self.recordQuestionCallback, ChoiceBox, title=_("Start recording?"), list=[(_("add recording (indefinitely)"), "indefinitely"), (_("add recording (stop after current event)"), "event"), (_("add recording (enter recording duration)"), "manualduration"),(_("don't record"), "no")])
1150
1151 from Screens.AudioSelection import AudioSelection
1152
1153 class InfoBarAudioSelection:
1154         def __init__(self):
1155                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1156                         {
1157                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1158                         })
1159
1160         def audioSelection(self):
1161                 service = self.session.nav.getCurrentService()
1162                 audio = service.audioTracks()
1163                 n = audio.getNumberOfTracks()
1164                 if n > 0:
1165                         self.session.open(AudioSelection, audio)
1166
1167 class InfoBarSubserviceSelection:
1168         def __init__(self):
1169                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1170                         {
1171                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1172                         })
1173
1174         def subserviceSelection(self):
1175                 service = self.session.nav.getCurrentService()
1176                 subservices = service.subServices()
1177                 
1178                 n = subservices.getNumberOfSubservices()
1179                 selection = 0
1180                 if n > 0:
1181                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1182                         tlist = []
1183                         for x in range(n):
1184                                 i = subservices.getSubservice(x)
1185                                 if i.toString() == ref.toString():
1186                                         selection = x
1187                                 tlist.append((i.getName(), i))
1188
1189                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection)
1190
1191         def subserviceSelected(self, service):
1192                 if not service is None:
1193                         self.session.nav.playService(service[1])
1194
1195 class InfoBarAdditionalInfo:
1196         def __init__(self):
1197                 self["DolbyActive"] = Pixmap()
1198                 self["CryptActive"] = Pixmap()
1199                 self["FormatActive"] = Pixmap()
1200                 
1201                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1202                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1203                 self.onLayoutFinish.append(self["ButtonRed"].update)
1204                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1205                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1206                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1207
1208                 self["ButtonGreen"] = Pixmap()
1209                 self["ButtonGreenText"] = Label(_("Subservices"))
1210
1211                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1212                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1213                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1214                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1215                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1216                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1217
1218                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1219                 self["ButtonBlue"].setConnect(lambda: True)
1220                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1221                 self["ButtonBlueText"].setConnect(lambda: True)
1222                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1223                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1224
1225                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1226
1227         def hideSubServiceIndication(self):
1228                 self["ButtonGreen"].hide()
1229                 self["ButtonGreenText"].hide()
1230
1231         def showSubServiceIndication(self):
1232                 self["ButtonGreen"].show()
1233                 self["ButtonGreenText"].show()
1234
1235         def checkFormat(self, service):
1236                 info = service.info()
1237                 if info is not None:
1238                         aspect = info.getInfo(iServiceInformation.sAspect)
1239                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1240                                 self["FormatActive"].show()
1241                         else:
1242                                 self["FormatActive"].hide()
1243
1244         def checkSubservices(self, service):
1245                 if service.subServices().getNumberOfSubservices() > 0:
1246                         self.showSubServiceIndication()
1247                 else:
1248                         self.hideSubServiceIndication()
1249
1250         def checkDolby(self, service):
1251                 # FIXME
1252                 dolby = False
1253                 audio = service.audioTracks()
1254                 if audio is not None:
1255                         n = audio.getNumberOfTracks()
1256                         for x in range(n):
1257                                 i = audio.getTrackInfo(x)
1258                                 description = i.getDescription();
1259                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1260                                         dolby = True
1261                                         break
1262                 if dolby:
1263                         self["DolbyActive"].show()
1264                 else:
1265                         self["DolbyActive"].hide()
1266
1267         def checkCrypted(self, service):
1268                 info = service.info()
1269                 if info is not None:
1270                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1271                                 self["CryptActive"].show()
1272                         else:
1273                                 self["CryptActive"].hide()
1274
1275         def gotServiceEvent(self, ev):
1276                 service = self.session.nav.getCurrentService()
1277                 if ev == iPlayableService.evUpdatedEventInfo:
1278                         self.checkSubservices(service)
1279                         self.checkFormat(service)
1280                 elif ev == iPlayableService.evUpdatedInfo:
1281                         self.checkCrypted(service)
1282                         self.checkDolby(service)
1283                 elif ev == iPlayableService.evEnd:
1284                         self.hideSubServiceIndication()
1285                         self["CryptActive"].hide()
1286                         self["DolbyActive"].hide()
1287                         self["FormatActive"].hide()
1288
1289 class InfoBarNotifications:
1290         def __init__(self):
1291                 self.onExecBegin.append(self.checkNotifications)
1292                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1293         
1294         def checkNotificationsIfExecing(self):
1295                 if self.execing:
1296                         self.checkNotifications()
1297
1298         def checkNotifications(self):
1299                 if len(Notifications.notifications):
1300                         n = Notifications.notifications[0]
1301                         Notifications.notifications = Notifications.notifications[1:]
1302                         print "open",n
1303                         cb = n[0]
1304                         if cb is not None:
1305                                 self.session.openWithCallback(cb, *n[1:])
1306                         else:
1307                                 self.session.open(*n[1:])
1308
1309 class InfoBarServiceNotifications:
1310         def __init__(self):
1311                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1312                         {
1313                                 iPlayableService.evEnd: self.serviceHasEnded
1314                         })
1315
1316         def serviceHasEnded(self):
1317                 print "service end!"
1318
1319                 try:
1320                         self.setSeekState(self.SEEK_STATE_PLAY)
1321                 except:
1322                         pass
1323
1324 class InfoBarCueSheetSupport:
1325         CUT_TYPE_IN = 0
1326         CUT_TYPE_OUT = 1
1327         CUT_TYPE_MARK = 2
1328         
1329         def __init__(self):
1330                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1331                         {
1332                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1333                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1334                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1335                         }, prio=1) 
1336                 
1337                 self.cut_list = [ ]
1338                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1339                         {
1340                                 iPlayableService.evStart: self.__serviceStarted,
1341                         })
1342
1343         def __serviceStarted(self):
1344                 print "new service started! trying to download cuts!"
1345                 self.downloadCuesheet()
1346
1347         def __getSeekable(self):
1348                 service = self.session.nav.getCurrentService()
1349                 if service is None:
1350                         return None
1351                 return service.seek()
1352
1353         def cueGetCurrentPosition(self):
1354                 seek = self.__getSeekable()
1355                 if seek is None:
1356                         return None
1357                 r = seek.getPlayPosition()
1358                 if r[0]:
1359                         return None
1360                 return long(r[1])
1361
1362         def jumpPreviousNextMark(self, cmp, alternative=None):
1363                 current_pos = self.cueGetCurrentPosition()
1364                 if current_pos is None:
1365                         return
1366                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1367                 if mark is not None:
1368                         pts = mark[0]
1369                 elif alternative is not None:
1370                         pts = alternative
1371                 else:
1372                         return
1373
1374                 seekable = self.__getSeekable()
1375                 if seekable is not None:
1376                         seekable.seekTo(pts)
1377
1378         def jumpPreviousMark(self):
1379                 # we add 2 seconds, so if the play position is <2s after
1380                 # the mark, the mark before will be used
1381                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1382
1383         def jumpNextMark(self):
1384                 self.jumpPreviousNextMark(lambda x: x)
1385
1386         def getNearestCutPoint(self, pts, cmp=abs):
1387                 # can be optimized
1388                 nearest = None
1389                 for cp in self.cut_list:
1390                         diff = cmp(cp[0] - pts)
1391                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1392                                 nearest = cp
1393                 return nearest
1394
1395         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1396                 current_pos = self.cueGetCurrentPosition()
1397                 if current_pos is None:
1398                         print "not seekable"
1399                         return
1400                 
1401                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1402                 
1403                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1404                         if onlyreturn:
1405                                 return nearest_cutpoint
1406                         if not onlyadd:
1407                                 self.removeMark(nearest_cutpoint)
1408                 elif not onlyremove and not onlyreturn:
1409                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1410                 
1411                 if onlyreturn:
1412                         return None
1413
1414         def addMark(self, point):
1415                 bisect.insort(self.cut_list, point)
1416                 self.uploadCuesheet()
1417
1418         def removeMark(self, point):
1419                 self.cut_list.remove(point)
1420                 self.uploadCuesheet()
1421
1422         def __getCuesheet(self):
1423                 service = self.session.nav.getCurrentService()
1424                 if service is None:
1425                         return None
1426                 return service.cueSheet()
1427
1428         def uploadCuesheet(self):
1429                 cue = self.__getCuesheet()
1430
1431                 if cue is None:
1432                         print "upload failed, no cuesheet interface"
1433                         return
1434                 cue.setCutList(self.cut_list)
1435
1436         def downloadCuesheet(self):
1437                 cue = self.__getCuesheet()
1438
1439                 if cue is None:
1440                         print "upload failed, no cuesheet interface"
1441                         return
1442                 self.cut_list = cue.getCutList()
1443
1444 class InfoBarSummary(Screen):
1445         skin = """
1446         <screen position="0,0" size="132,64">
1447                 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1448                 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1449         </screen>"""
1450
1451         def __init__(self, session, parent):
1452                 Screen.__init__(self, session)
1453                 self["CurrentService"] = ServiceName(self.session.nav)
1454                 self["Clock"] = Clock()
1455
1456 class InfoBarSummarySupport:
1457         def __init__(self):
1458                 pass
1459         
1460         def createSummary(self):
1461                 return InfoBarSummary
1462
1463 class InfoBarTeletextPlugin:
1464         def __init__(self):
1465                 self.teletext_plugin = None
1466                 
1467                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1468                         self.teletext_plugin = p
1469                 
1470                 if self.teletext_plugin is not None:
1471                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1472                                 {
1473                                         "startTeletext": (self.startTeletext, "View teletext...")
1474                                 })
1475                 else:
1476                         print "no teletext plugin found!"
1477
1478         def startTeletext(self):
1479                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())