fix recursive close crashes
[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["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", 
347                         {
348                                 "showEventInfo": (self.openEventView, _("show EPG...")),
349                         })
350
351         def zapToService(self, service):
352                 if not service is None:
353                         if self.servicelist.getRoot() != self.epg_bouquet: #already in correct bouquet?
354                                 self.servicelist.clearPath()
355                                 if self.servicelist.bouquet_root != self.epg_bouquet:
356                                         self.servicelist.enterPath(self.servicelist.bouquet_root)
357                                 self.servicelist.enterPath(self.epg_bouquet)
358                         self.servicelist.setCurrentSelection(service) #select the service in servicelist
359                         self.servicelist.zap()
360
361         def openBouquetEPG(self, bouquet, withCallback=True):
362                 ptr=eEPGCache.getInstance()
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                 if len(services):
374                         self.epg_bouquet = bouquet
375                         if withCallback:
376                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, EPGSelection, services, self.zapToService))
377                         else:
378                                 self.session.open(EPGSelection, services, self.zapToService)
379
380         def closed(self, ret):
381                 self.dlg_stack.pop()
382                 if ret:
383                         dlgs=len(self.dlg_stack)
384                         assert dlgs>0
385                         self.dlg_stack[dlgs-1].close(dlgs > 1)
386
387         def openMultiServiceEPG(self, withCallback=True):
388                 bouquets = self.servicelist.getBouquetList()
389                 if bouquets is None:
390                         cnt = 0
391                 else:
392                         cnt = len(bouquets)
393                 if cnt > 1: # show bouquet list
394                         if withCallback:
395                                 self.dlg_stack.append(self.session.openWithCallback(self.closed, BouquetSelector, bouquets, self.openBouquetEPG))
396                         else:
397                                 self.session.open(BouquetSelector, bouquets, self.openBouquetEPG)
398                 elif cnt == 1: 
399                         self.openBouquetEPG(bouquets[0][1], withCallback)
400
401         def openSingleServiceEPG(self):
402                 ref=self.session.nav.getCurrentlyPlayingServiceReference()
403                 self.session.open(EPGSelection, ref)
404
405         def openSimilarList(self, eventid, refstr):
406                 self.session.open(EPGSelection, refstr, None, eventid)
407
408         def openEventView(self):
409                 self.epglist = [ ]
410                 service = self.session.nav.getCurrentService()
411                 ref = self.session.nav.getCurrentlyPlayingServiceReference()
412                 info = service.info()
413                 ptr=info.getEvent(0)
414                 if ptr:
415                         self.epglist.append(ptr)
416                 ptr=info.getEvent(1)
417                 if ptr:
418                         self.epglist.append(ptr)
419                 if len(self.epglist) == 0:
420                         epg = eEPGCache.getInstance()
421                         ptr = epg.lookupEventTime(ref, -1)
422                         if ptr:
423                                 self.epglist.append(ptr)
424                                 ptr = epg.lookupEventTime(ref, ptr.getBeginTime(), +1)
425                                 if ptr:
426                                         self.epglist.append(ptr)
427                 if len(self.epglist) > 0:
428                         self.dlg_stack.append(self.session.openWithCallback(self.closed, EventViewEPGSelect, self.epglist[0], ServiceReference(ref), self.eventViewCallback, self.openSingleServiceEPG, self.openMultiServiceEPG, self.openSimilarList))
429                 else:
430                         print "no epg for the service avail.. so we show multiepg instead of eventinfo"
431                         self.openMultiServiceEPG(False)
432
433         def eventViewCallback(self, setEvent, setService, val): #used for now/next displaying
434                 if len(self.epglist) > 1:
435                         tmp = self.epglist[0]
436                         self.epglist[0]=self.epglist[1]
437                         self.epglist[1]=tmp
438                         setEvent(self.epglist[0])
439
440 class InfoBarTuner:
441         """provides a snr/agc/ber display"""
442         def __init__(self):
443                 self["snr"] = Label()
444                 self["agc"] = Label()
445                 self["ber"] = Label()
446                 self["snr_percent"] = TunerInfo(TunerInfo.SNR_PERCENTAGE, servicefkt = self.session.nav.getCurrentService)
447                 self["agc_percent"] = TunerInfo(TunerInfo.AGC_PERCENTAGE, servicefkt = self.session.nav.getCurrentService)
448                 self["ber_count"] = TunerInfo(TunerInfo.BER_VALUE, servicefkt = self.session.nav.getCurrentService)
449                 self["snr_progress"] = TunerInfo(TunerInfo.SNR_BAR, servicefkt = self.session.nav.getCurrentService)
450                 self["agc_progress"] = TunerInfo(TunerInfo.AGC_BAR, servicefkt = self.session.nav.getCurrentService)
451                 self["ber_progress"] = TunerInfo(TunerInfo.BER_BAR, servicefkt = self.session.nav.getCurrentService)
452                 self.timer = eTimer()
453                 self.timer.timeout.get().append(self.updateTunerInfo)
454                 self.timer.start(1000)
455
456         def updateTunerInfo(self):
457                 if self.instance.isVisible():
458                         self["snr_percent"].update()
459                         self["agc_percent"].update()
460                         self["ber_count"].update()
461                         self["snr_progress"].update()
462                         self["agc_progress"].update()
463                         self["ber_progress"].update()
464
465 class InfoBarEvent:
466         """provides a current/next event info display"""
467         def __init__(self):
468                 self["Event_Now_StartTime"] = EventInfo(self.session.nav, EventInfo.Now_StartTime)
469                 self["Event_Next_StartTime"] = EventInfo(self.session.nav, EventInfo.Next_StartTime)
470                                 
471                 self["Event_Now"] = EventInfo(self.session.nav, EventInfo.Now)
472                 self["Event_Next"] = EventInfo(self.session.nav, EventInfo.Next)
473
474                 self["Event_Now_Duration"] = EventInfo(self.session.nav, EventInfo.Now_Remaining)
475                 self["Event_Next_Duration"] = EventInfo(self.session.nav, EventInfo.Next_Duration)
476
477                 self["Now_ProgressBar"] = EventInfoProgress(self.session.nav, EventInfo.Now)
478
479 class InfoBarServiceName:
480         def __init__(self):
481                 self["ServiceName"] = ServiceName(self.session.nav)
482
483 class InfoBarSeek:
484         """handles actions like seeking, pause"""
485         
486         # ispause, isff, issm
487         SEEK_STATE_PLAY = (0, 0, 0, ">")
488         SEEK_STATE_PAUSE = (1, 0, 0, "||")
489         SEEK_STATE_FF_2X = (0, 2, 0, ">> 2x")
490         SEEK_STATE_FF_4X = (0, 4, 0, ">> 4x")
491         SEEK_STATE_FF_8X = (0, 8, 0, ">> 8x")
492         SEEK_STATE_FF_32X = (0, 32, 0, ">> 32x")
493         SEEK_STATE_FF_64X = (0, 64, 0, ">> 64x")
494         SEEK_STATE_FF_128X = (0, 128, 0, ">> 128x")
495         
496         SEEK_STATE_BACK_16X = (0, -16, 0, "<< 16x")
497         SEEK_STATE_BACK_32X = (0, -32, 0, "<< 32x")
498         SEEK_STATE_BACK_64X = (0, -64, 0, "<< 64x")
499         SEEK_STATE_BACK_128X = (0, -128, 0, "<< 128x")
500         
501         SEEK_STATE_SM_HALF = (0, 0, 2, "/2")
502         SEEK_STATE_SM_QUARTER = (0, 0, 4, "/4")
503         SEEK_STATE_SM_EIGHTH = (0, 0, 8, "/8")
504         
505         def __init__(self):
506                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
507                         {
508                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged,
509                                 iPlayableService.evStart: self.__serviceStarted,
510                                 
511                                 iPlayableService.evEOF: self.__evEOF,
512                                 iPlayableService.evSOF: self.__evSOF,
513                         })
514
515                 class InfoBarSeekActionMap(HelpableActionMap):
516                         def __init__(self, screen, *args, **kwargs):
517                                 HelpableActionMap.__init__(self, screen, *args, **kwargs)
518                                 self.screen = screen
519                                 
520                         def action(self, contexts, action):
521                                 if action[:5] == "seek:":
522                                         time = int(action[5:])
523                                         self.screen.seekRelative(time * 90000)
524                                         return 1
525                                 else:
526                                         return HelpableActionMap.action(self, contexts, action)
527
528                 self["SeekActions"] = InfoBarSeekActionMap(self, "InfobarSeekActions", 
529                         {
530                                 "pauseService": (self.pauseService, "pause"),
531                                 "unPauseService": (self.unPauseService, "continue"),
532                                 
533                                 "seekFwd": (self.seekFwd, "skip forward"),
534                                 "seekFwdDown": self.seekFwdDown,
535                                 "seekFwdUp": self.seekFwdUp,
536                                 "seekBack": (self.seekBack, "skip backward"),
537                                 "seekBackDown": self.seekBackDown,
538                                 "seekBackUp": self.seekBackUp,
539                         }, prio=-1)
540                         # give them a little more priority to win over color buttons
541
542                 self.seekstate = self.SEEK_STATE_PLAY
543                 self.onClose.append(self.delTimer)
544                 
545                 self.fwdtimer = False
546                 self.fwdKeyTimer = eTimer()
547                 self.fwdKeyTimer.timeout.get().append(self.fwdTimerFire)
548
549                 self.rwdtimer = False
550                 self.rwdKeyTimer = eTimer()
551                 self.rwdKeyTimer.timeout.get().append(self.rwdTimerFire)
552                 
553                 self.onPlayStateChanged = [ ]
554                 
555                 self.lockedBecauseOfSkipping = False
556         
557         def up(self):
558                 pass
559         
560         def down(self):
561                 pass
562         
563         def delTimer(self):
564                 del self.fwdKeyTimer
565                 del self.rwdKeyTimer
566         
567         def getSeek(self):
568                 service = self.session.nav.getCurrentService()
569                 if service is None:
570                         return None
571
572                 seek = service.seek()
573
574                 if seek is None or not seek.isCurrentlySeekable():
575                         return None
576                 
577                 return seek
578         
579         def isSeekable(self):
580                 if self.getSeek() is None:
581                         return False
582                 return True
583
584         def __seekableStatusChanged(self):
585                 print "seekable status changed!"
586                 if not self.isSeekable():
587                         self["SeekActions"].setEnabled(False)
588                         print "not seekable, return to play"
589                         self.setSeekState(self.SEEK_STATE_PLAY)
590                 else:
591                         self["SeekActions"].setEnabled(True)
592                         print "seekable"
593
594         def __serviceStarted(self):
595                 self.seekstate = self.SEEK_STATE_PLAY
596
597         def setSeekState(self, state):
598                 service = self.session.nav.getCurrentService()
599                 
600                 if service is None:
601                         return False
602                 
603                 if not self.isSeekable():
604                         if state not in [self.SEEK_STATE_PLAY, self.SEEK_STATE_PAUSE]:
605                                 state = self.SEEK_STATE_PLAY
606                 
607                 pauseable = service.pause()
608
609                 if pauseable is None:
610                         print "not pauseable."
611                         state = self.SEEK_STATE_PLAY
612                 
613                 oldstate = self.seekstate
614                 self.seekstate = state
615                 
616                 for i in range(3):
617                         if oldstate[i] != self.seekstate[i]:
618                                 (self.session.nav.pause, pauseable.setFastForward, pauseable.setSlowMotion)[i](self.seekstate[i])
619
620                 for c in self.onPlayStateChanged:
621                         c(self.seekstate)
622                 
623                 self.checkSkipShowHideLock()
624
625                 return True
626
627         def pauseService(self):
628                 if self.seekstate == self.SEEK_STATE_PAUSE:
629                         print "pause, but in fact unpause"
630                         self.unPauseService()
631                 else:
632                         if self.seekstate == self.SEEK_STATE_PLAY:
633                                 print "yes, playing."
634                         else:
635                                 print "no", self.seekstate
636                         print "pause"
637                         self.setSeekState(self.SEEK_STATE_PAUSE);
638                 
639         def unPauseService(self):
640                 print "unpause"
641                 self.setSeekState(self.SEEK_STATE_PLAY);
642         
643         def doSeek(self, seektime):
644                 print "doseek", seektime
645                 service = self.session.nav.getCurrentService()
646                 if service is None:
647                         return
648                 
649                 seekable = self.getSeek()
650                 if seekable is None:
651                         return
652                 
653                 seekable.seekTo(90 * seektime)
654
655         def seekFwdDown(self):
656                 print "start fwd timer"
657                 self.fwdtimer = True
658                 self.fwdKeyTimer.start(1000)
659
660         def seekBackDown(self):
661                 print "start rewind timer"
662                 self.rwdtimer = True
663                 self.rwdKeyTimer.start(1000)
664
665         def seekFwdUp(self):
666                 print "seekFwdUp"
667                 if self.fwdtimer:
668                         self.fwdKeyTimer.stop()
669                         self.fwdtimer = False
670                         self.seekFwd()
671
672         def seekFwd(self):
673                 lookup = {
674                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_FF_2X,
675                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_SM_EIGHTH,
676                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_FF_4X,
677                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_8X,
678                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_32X,
679                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_64X,
680                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_128X,
681                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_128X,
682                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_PLAY,
683                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_16X,
684                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_32X,
685                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_64X,
686                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_HALF,
687                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_HALF,
688                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_SM_QUARTER
689                         }
690                 self.setSeekState(lookup[self.seekstate])
691         
692         def seekBackUp(self):
693                 print "seekBackUp"
694                 if self.rwdtimer:
695                         self.rwdKeyTimer.stop()
696                         self.rwdtimer = False
697                         self.seekBack()
698                 
699         def seekBack(self):
700                 lookup = {
701                                 self.SEEK_STATE_PLAY: self.SEEK_STATE_BACK_16X,
702                                 self.SEEK_STATE_PAUSE: self.SEEK_STATE_PAUSE,
703                                 self.SEEK_STATE_FF_2X: self.SEEK_STATE_PLAY,
704                                 self.SEEK_STATE_FF_4X: self.SEEK_STATE_FF_2X,
705                                 self.SEEK_STATE_FF_8X: self.SEEK_STATE_FF_4X,
706                                 self.SEEK_STATE_FF_32X: self.SEEK_STATE_FF_8X,
707                                 self.SEEK_STATE_FF_64X: self.SEEK_STATE_FF_32X,
708                                 self.SEEK_STATE_FF_128X: self.SEEK_STATE_FF_64X,
709                                 self.SEEK_STATE_BACK_16X: self.SEEK_STATE_BACK_32X,
710                                 self.SEEK_STATE_BACK_32X: self.SEEK_STATE_BACK_64X,
711                                 self.SEEK_STATE_BACK_64X: self.SEEK_STATE_BACK_128X,
712                                 self.SEEK_STATE_BACK_128X: self.SEEK_STATE_BACK_128X,
713                                 self.SEEK_STATE_SM_HALF: self.SEEK_STATE_SM_QUARTER,
714                                 self.SEEK_STATE_SM_QUARTER: self.SEEK_STATE_SM_EIGHTH,
715                                 self.SEEK_STATE_SM_EIGHTH: self.SEEK_STATE_PAUSE
716                         }
717                 self.setSeekState(lookup[self.seekstate])
718                 
719                 if self.seekstate == self.SEEK_STATE_PAUSE:
720                         seekable = self.getSeek()
721                         if seekable is not None:
722                                 seekable.seekRelative(-1, 3)
723
724         def fwdTimerFire(self):
725                 print "Display seek fwd"
726                 self.fwdKeyTimer.stop()
727                 self.fwdtimer = False
728                 self.session.openWithCallback(self.fwdSeekTo, MinuteInput)
729                 
730         def fwdSeekTo(self, minutes):
731                 print "Seek", minutes, "minutes forward"
732                 if minutes != 0:
733                         seekable = self.getSeek()
734                         if seekable is not None:
735                                 seekable.seekRelative(1, minutes * 60 * 90000)
736         
737         def rwdTimerFire(self):
738                 print "rwdTimerFire"
739                 self.rwdKeyTimer.stop()
740                 self.rwdtimer = False
741                 self.session.openWithCallback(self.rwdSeekTo, MinuteInput)
742         
743         def rwdSeekTo(self, minutes):
744                 print "rwdSeekTo"
745                 self.fwdSeekTo(0 - minutes)
746         
747         def checkSkipShowHideLock(self):
748                 wantlock = self.seekstate != self.SEEK_STATE_PLAY
749                 
750                 if self.lockedBecauseOfSkipping and not wantlock:
751                         self.unlockShow()
752                         self.lockedBecauseOfSkipping = False
753                 
754                 if wantlock and not self.lockedBecauseOfSkipping:
755                         self.lockShow()
756                         self.lockedBecauseOfSkipping = True
757
758         def __evEOF(self):
759                 if self.seekstate != self.SEEK_STATE_PLAY:
760                         self.setSeekState(self.SEEK_STATE_PAUSE)
761                         # HACK
762                         #self.getSeek().seekRelative(1, -90000)
763                         self.setSeekState(self.SEEK_STATE_PLAY)
764                 else:
765                         self.setSeekState(self.SEEK_STATE_PAUSE)
766         
767         def __evSOF(self):
768                 self.setSeekState(self.SEEK_STATE_PLAY)
769                 self.doSeek(0)
770
771         def seekRelative(self, diff):
772                 seekable = self.getSeek()
773                 if seekable is not None:
774                         seekable.seekRelative(1, diff)
775
776 from Screens.PVRState import PVRState, TimeshiftState
777
778 class InfoBarPVRState:
779         def __init__(self, screen=PVRState):
780                 self.onPlayStateChanged.append(self.__playStateChanged)
781                 self.pvrStateDialog = self.session.instantiateDialog(screen)
782                 self.onShow.append(self.__mayShow)
783                 self.onHide.append(self.pvrStateDialog.hide)
784         
785         def __mayShow(self):
786                 if self.seekstate != self.SEEK_STATE_PLAY and self.execing:
787                         self.pvrStateDialog.show()
788
789         def __playStateChanged(self, state):
790                 playstateString = state[3]
791                 self.pvrStateDialog["state"].setText(playstateString)
792                 self.__mayShow()
793
794 class InfoBarTimeshiftState(InfoBarPVRState):
795         def __init__(self):
796                 InfoBarPVRState.__init__(self, screen=TimeshiftState)
797
798
799 class InfoBarShowMovies:
800
801         # i don't really like this class. 
802         # it calls a not further specified "movie list" on up/down/movieList,
803         # so this is not more than an action map
804         def __init__(self):
805                 self["MovieListActions"] = HelpableActionMap(self, "InfobarMovieListActions", 
806                         {
807                                 "movieList": (self.showMovies, "movie list"),
808                                 "up": (self.showMovies, "movie list"),
809                                 "down": (self.showMovies, "movie list")
810                         })
811
812 # InfoBarTimeshift requires InfoBarSeek, instantiated BEFORE!
813
814 # Hrmf.
815 #
816 # Timeshift works the following way:
817 #                                         demux0   demux1                    "TimeshiftActions" "TimeshiftActivateActions" "SeekActions"
818 # - normal playback                       TUNER    unused      PLAY               enable                disable              disable
819 # - user presses "yellow" button.         TUNER    record      PAUSE              enable                disable              enable
820 # - user presess pause again              FILE     record      PLAY               enable                disable              enable
821 # - user fast forwards                    FILE     record      FF                 enable                disable              enable
822 # - end of timeshift buffer reached       TUNER    record      PLAY               enable                enable               disable
823 # - user backwards                        FILE     record      BACK  # !!         enable                disable              enable
824 #
825
826 # in other words:
827 # - when a service is playing, pressing the "timeshiftStart" button ("yellow") enables recording ("enables timeshift"),
828 # freezes the picture (to indicate timeshift), sets timeshiftMode ("activates timeshift")
829 # now, the service becomes seekable, so "SeekActions" are enabled, "TimeshiftEnableActions" are disabled.
830 # - the user can now PVR around
831 # - if it hits the end, the service goes into live mode ("deactivates timeshift", it's of course still "enabled")
832 # the service looses it's "seekable" state. It can still be paused, but just to activate timeshift right
833 # after!
834 # the seek actions will be disabled, but the timeshiftActivateActions will be enabled
835 # - if the user rewinds, or press pause, timeshift will be activated again
836
837 # note that a timeshift can be enabled ("recording") and
838 # activated (currently time-shifting).
839
840 class InfoBarTimeshift:
841         def __init__(self):
842                 self["TimeshiftActions"] = HelpableActionMap(self, "InfobarTimeshiftActions", 
843                         {
844                                 "timeshiftStart": (self.startTimeshift, "start timeshift"),  # the "yellow key"
845                                 "timeshiftStop": (self.stopTimeshift, "stop timeshift")      # currently undefined :), probably 'TV'
846                         }, prio=1)
847                 self["TimeshiftActivateActions"] = ActionMap(["InfobarTimeshiftActivateActions"],
848                         {
849                                 "timeshiftActivateEnd": self.activateTimeshiftEnd, # something like "pause key"
850                                 "timeshiftActivateEndAndPause": self.activateTimeshiftEndAndPause  # something like "backward key"
851                         }, prio=-1) # priority over record
852
853                 self.timeshift_enabled = 0
854                 self.timeshift_state = 0
855                 self.ts_pause_timer = eTimer()
856                 self.ts_pause_timer.timeout.get().append(self.pauseService)
857
858                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
859                         {
860                                 iPlayableService.evStart: self.__serviceStarted,
861                                 iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged
862                         })
863         
864         def getTimeshift(self):
865                 service = self.session.nav.getCurrentService()
866                 return service and service.timeshift()
867
868         def startTimeshift(self):
869                 print "enable timeshift"
870                 ts = self.getTimeshift()
871                 if ts is None:
872                         self.session.open(MessageBox, _("Timeshift not possible!"), MessageBox.TYPE_ERROR)
873                         print "no ts interface"
874                         return
875                 
876                 if self.timeshift_enabled:
877                         print "hu, timeshift already enabled?"
878                 else:
879                         if not ts.startTimeshift():
880                                 import time
881                                 self.timeshift_enabled = 1
882                                 self.pvrStateDialog["timeshift"].setRelative(time.time())
883                                 
884                                 # PAUSE.
885                                 self.setSeekState(self.SEEK_STATE_PAUSE)
886                                 
887                                 # enable the "TimeshiftEnableActions", which will override
888                                 # the startTimeshift actions
889                                 self.__seekableStatusChanged()
890                         else:
891                                 print "timeshift failed"
892
893         def stopTimeshift(self):
894                 if not self.timeshift_enabled:
895                         return
896                 print "disable timeshift"
897                 ts = self.getTimeshift()
898                 if ts is None:
899                         return
900                 self.session.openWithCallback(self.stopTimeshiftConfirmed, MessageBox, _("Stop Timeshift?"), MessageBox.TYPE_YESNO)
901
902         def stopTimeshiftConfirmed(self, confirmed):
903                 if not confirmed:
904                         return
905
906                 ts = self.getTimeshift()
907                 if ts is None:
908                         return
909
910                 ts.stopTimeshift()
911                 self.timeshift_enabled = 0
912
913                 # disable actions
914                 self.__seekableStatusChanged()
915         
916         # activates timeshift, and seeks to (almost) the end
917         def activateTimeshiftEnd(self):
918                 ts = self.getTimeshift()
919                 
920                 if ts is None:
921                         return
922                 
923                 if ts.isTimeshiftActive():
924                         print "!! activate timeshift called - but shouldn't this be a normal pause?"
925                         self.pauseService()
926                 else:
927                         self.setSeekState(self.SEEK_STATE_PLAY)
928                         ts.activateTimeshift()
929                         self.seekRelative(0)
930         
931         # same as activateTimeshiftEnd, but pauses afterwards.
932         def activateTimeshiftEndAndPause(self):
933                 state = self.seekstate
934                 self.activateTimeshiftEnd()
935                 
936                 # well, this is "andPause", but it could be pressed from pause,
937                 # when pausing on the (fake-)"live" picture, so an un-pause
938                 # is perfectly ok.
939                 
940                 print "now, pauseService"
941                 if state == self.SEEK_STATE_PLAY:
942                         print "is PLAYING, start pause timer"
943                         self.ts_pause_timer.start(200, 1)
944                 else:
945                         print "unpause"
946                         self.unPauseService()
947         
948         def __seekableStatusChanged(self):
949                 enabled = False
950                 
951                 print "self.isSeekable", self.isSeekable()
952                 print "self.timeshift_enabled", self.timeshift_enabled
953                 
954                 # when this service is not seekable, but timeshift
955                 # is enabled, this means we can activate
956                 # the timeshift
957                 if not self.isSeekable() and self.timeshift_enabled:
958                         enabled = True
959
960                 print "timeshift activate:", enabled
961                 self["TimeshiftActivateActions"].setEnabled(enabled)
962
963         def __serviceStarted(self):
964                 self.timeshift_enabled = False
965                 self.__seekableStatusChanged()
966
967 class InfoBarExtensions:
968         def __init__(self):
969                 self.pipshown = False
970                 
971                 self["InstantExtensionsActions"] = HelpableActionMap(self, "InfobarExtensions",
972                         {
973                                 #"extensions": (self.extensions, "Extensions..."),
974                         })
975                         
976         def extensions(self):
977                 list = []
978                 if self.pipshown == False:
979                         list.append((_("Activate Picture in Picture"), "pipon"))
980                 elif self.pipshown == True:
981                         list.append((_("Disable Picture in Picture"), "pipoff"))
982                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list)
983
984         def extensionCallback(self, answer):
985                 if answer is not None:
986                         if answer[1] == "pipon":
987                                 self.session.nav.stopService()
988                                 self.pip = self.session.instantiateDialog(PictureInPicture)
989                                 #self.pip.show()
990                                 
991                                 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
992                                 self.pipservice = eServiceCenter.getInstance().play(newservice)
993                                 if self.pipservice and not self.pipservice.setTarget(1):
994                                         self.pipservice.start()
995                                         self.pipshown = True
996                                 else:
997                                         self.pipservice = None
998                                         del self.pip
999         
1000                         elif answer[1] == "pipoff":
1001                                 #self.pip.hide()
1002                                 self.pipservice = None
1003                                 del self.pip
1004                                 self.pipshown = False
1005
1006 from RecordTimer import parseEvent
1007
1008 class InfoBarInstantRecord:
1009         """Instant Record - handles the instantRecord action in order to 
1010         start/stop instant records"""
1011         def __init__(self):
1012                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1013                         {
1014                                 "instantRecord": (self.instantRecord, "Instant Record..."),
1015                         })
1016                 self.recording = []
1017                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1018                 self["BlinkingPoint"].hide()
1019                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1020
1021         def stopCurrentRecording(self, entry = -1):     
1022                 if entry is not None and entry != -1:
1023                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1024                         self.recording.remove(self.recording[entry])
1025                         
1026         def startInstantRecording(self, limitEvent = False):
1027                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1028                 
1029                 # try to get event info
1030                 event = None
1031                 try:
1032                         service = self.session.nav.getCurrentService()
1033                         epg = eEPGCache.getInstance()
1034                         event = epg.lookupEventTime(serviceref, -1, 0)
1035                         if event is None:
1036                                 info = service.info()
1037                                 ev = info.getEvent(0)
1038                                 event = ev
1039                 except:
1040                         pass
1041
1042                 begin = time.time()
1043                 end = time.time() + 3600 * 10
1044                 name = "instant record"
1045                 description = ""
1046                 eventid = None
1047                 
1048                 if event is not None:
1049                         curEvent = parseEvent(event)
1050                         name = curEvent[2]
1051                         description = curEvent[3]
1052                         eventid = curEvent[4]
1053                         if limitEvent:
1054                                 end = curEvent[1]
1055                 else:
1056                         if limitEvent:
1057                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1058                                 
1059                 data = (begin, end, name, description, eventid)
1060                 
1061                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1062                 recording.dontSave = True
1063                 self.recording.append(recording)
1064                 
1065                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1066                 
1067         def isInstantRecordRunning(self):
1068                 print "self.recording:", self.recording
1069                 if len(self.recording) > 0:
1070                         for x in self.recording:
1071                                 if x.isRunning():
1072                                         return True
1073                 return False
1074
1075         def recordQuestionCallback(self, answer):
1076                 print "pre:\n", self.recording
1077                 
1078                 if answer is None or answer[1] == "no":
1079                         return
1080                 list = []
1081                 recording = self.recording[:]
1082                 for x in recording:
1083                         if not x in self.session.nav.RecordTimer.timer_list:
1084                                 self.recording.remove(x)
1085                         elif x.dontSave and x.isRunning():
1086                                 list.append(TimerEntryComponent(x, False))              
1087
1088                 if answer[1] == "changeduration":
1089                         if len(self.recording) == 1:
1090                                 self.changeDuration(0)
1091                         else:
1092                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1093                 elif answer[1] == "stop":
1094                         if len(self.recording) == 1:
1095                                 self.stopCurrentRecording(0)
1096                         else:
1097                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1098                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1099                         limitEvent = False
1100                         if answer[1] == "event":
1101                                 limitEvent = True
1102                         if answer[1] == "manualduration":
1103                                 self.selectedEntry = len(self.recording)
1104                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1105                         self.startInstantRecording(limitEvent = limitEvent)
1106                         
1107                 print "after:\n", self.recording
1108
1109         def changeDuration(self, entry):
1110                 if entry is not None:
1111                         self.selectedEntry = entry
1112                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1113
1114         def inputCallback(self, value):
1115                 if value is not None:
1116                         print "stopping recording after", int(value), "minutes."
1117                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1118                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1119
1120         def instantRecord(self):
1121                 try:
1122                         stat = os.stat(resolveFilename(SCOPE_HDD))
1123                 except:
1124                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1125                         return
1126         
1127                 if self.isInstantRecordRunning():
1128                         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")])
1129                 else:
1130                         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")])
1131
1132 from Screens.AudioSelection import AudioSelection
1133
1134 class InfoBarAudioSelection:
1135         def __init__(self):
1136                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1137                         {
1138                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1139                         })
1140
1141         def audioSelection(self):
1142                 service = self.session.nav.getCurrentService()
1143                 audio = service.audioTracks()
1144                 n = audio.getNumberOfTracks()
1145                 if n > 0:
1146                         self.session.open(AudioSelection, audio)
1147
1148 from Screens.SubserviceSelection import SubserviceSelection
1149
1150 class InfoBarSubserviceSelection:
1151         def __init__(self):
1152                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1153                         {
1154                                 "subserviceSelection": (self.subserviceSelection, "Subservice list..."),
1155                         })
1156
1157         def subserviceSelection(self):
1158                 service = self.session.nav.getCurrentService()
1159                 subservices = service.subServices()
1160                 n = subservices.getNumberOfSubservices()
1161                 if n > 0:
1162                         self.session.openWithCallback(self.subserviceSelected, SubserviceSelection, subservices)
1163
1164         def subserviceSelected(self, service):
1165                 if not service is None:
1166                         self.session.nav.playService(service)
1167
1168 class InfoBarAdditionalInfo:
1169         def __init__(self):
1170                 self["DolbyActive"] = Pixmap()
1171                 self["CryptActive"] = Pixmap()
1172                 self["FormatActive"] = Pixmap()
1173                 
1174                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1175                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1176                 self.onLayoutFinish.append(self["ButtonRed"].update)
1177                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1178                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1179                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1180
1181                 self["ButtonGreen"] = Pixmap()
1182                 self["ButtonGreenText"] = Label(_("Subservices"))
1183
1184                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1185                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1186                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1187                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1188                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1189                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1190
1191                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1192                 self["ButtonBlue"].setConnect(lambda: False)
1193                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1194                 self["ButtonBlueText"].setConnect(lambda: False)
1195                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1196                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1197
1198                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1199
1200         def hideSubServiceIndication(self):
1201                 self["ButtonGreen"].hide()
1202                 self["ButtonGreenText"].hide()
1203
1204         def showSubServiceIndication(self):
1205                 self["ButtonGreen"].show()
1206                 self["ButtonGreenText"].show()
1207
1208         def checkFormat(self, service):
1209                 info = service.info()
1210                 if info is not None:
1211                         aspect = info.getInfo(iServiceInformation.sAspect)
1212                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1213                                 self["FormatActive"].show()
1214                         else:
1215                                 self["FormatActive"].hide()
1216
1217         def checkSubservices(self, service):
1218                 if service.subServices().getNumberOfSubservices() > 0:
1219                         self.showSubServiceIndication()
1220                 else:
1221                         self.hideSubServiceIndication()
1222
1223         def checkDolby(self, service):
1224                 # FIXME
1225                 dolby = False
1226                 audio = service.audioTracks()
1227                 if audio is not None:
1228                         n = audio.getNumberOfTracks()
1229                         for x in range(n):
1230                                 i = audio.getTrackInfo(x)
1231                                 description = i.getDescription();
1232                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1233                                         dolby = True
1234                                         break
1235                 if dolby:
1236                         self["DolbyActive"].show()
1237                 else:
1238                         self["DolbyActive"].hide()
1239
1240         def checkCrypted(self, service):
1241                 info = service.info()
1242                 if info is not None:
1243                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1244                                 self["CryptActive"].show()
1245                         else:
1246                                 self["CryptActive"].hide()
1247
1248         def gotServiceEvent(self, ev):
1249                 service = self.session.nav.getCurrentService()
1250                 if ev == iPlayableService.evUpdatedEventInfo:
1251                         self.checkSubservices(service)
1252                         self.checkFormat(service)
1253                 elif ev == iPlayableService.evUpdatedInfo:
1254                         self.checkCrypted(service)
1255                         self.checkDolby(service)
1256                 elif ev == iPlayableService.evEnd:
1257                         self.hideSubServiceIndication()
1258                         self["CryptActive"].hide()
1259                         self["DolbyActive"].hide()
1260                         self["FormatActive"].hide()
1261
1262 class InfoBarNotifications:
1263         def __init__(self):
1264                 self.onExecBegin.append(self.checkNotifications)
1265                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1266         
1267         def checkNotificationsIfExecing(self):
1268                 if self.execing:
1269                         self.checkNotifications()
1270
1271         def checkNotifications(self):
1272                 if len(Notifications.notifications):
1273                         n = Notifications.notifications[0]
1274                         Notifications.notifications = Notifications.notifications[1:]
1275                         print "open",n
1276                         cb = n[0]
1277                         if cb is not None:
1278                                 self.session.openWithCallback(cb, *n[1:])
1279                         else:
1280                                 self.session.open(*n[1:])
1281
1282 class InfoBarServiceNotifications:
1283         def __init__(self):
1284                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1285                         {
1286                                 iPlayableService.evEnd: self.serviceHasEnded
1287                         })
1288
1289         def serviceHasEnded(self):
1290                 print "service end!"
1291
1292                 try:
1293                         self.setSeekState(self.SEEK_STATE_PLAY)
1294                 except:
1295                         pass
1296
1297 class InfoBarCueSheetSupport:
1298         CUT_TYPE_IN = 0
1299         CUT_TYPE_OUT = 1
1300         CUT_TYPE_MARK = 2
1301         
1302         def __init__(self):
1303                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1304                         {
1305                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1306                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1307                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1308                         }, prio=1) 
1309                 
1310                 self.cut_list = [ ]
1311                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1312                         {
1313                                 iPlayableService.evStart: self.__serviceStarted,
1314                         })
1315
1316         def __serviceStarted(self):
1317                 print "new service started! trying to download cuts!"
1318                 self.downloadCuesheet()
1319
1320         def __getSeekable(self):
1321                 service = self.session.nav.getCurrentService()
1322                 if service is None:
1323                         return None
1324                 return service.seek()
1325
1326         def cueGetCurrentPosition(self):
1327                 seek = self.__getSeekable()
1328                 if seek is None:
1329                         return None
1330                 r = seek.getPlayPosition()
1331                 if r[0]:
1332                         return None
1333                 return long(r[1])
1334
1335         def jumpPreviousNextMark(self, cmp, alternative=None):
1336                 current_pos = self.cueGetCurrentPosition()
1337                 if current_pos is None:
1338                         return
1339                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1340                 if mark is not None:
1341                         pts = mark[0]
1342                 elif alternative is not None:
1343                         pts = alternative
1344                 else:
1345                         return
1346
1347                 seekable = self.__getSeekable()
1348                 if seekable is not None:
1349                         seekable.seekTo(pts)
1350
1351         def jumpPreviousMark(self):
1352                 # we add 2 seconds, so if the play position is <2s after
1353                 # the mark, the mark before will be used
1354                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1355
1356         def jumpNextMark(self):
1357                 self.jumpPreviousNextMark(lambda x: x)
1358
1359         def getNearestCutPoint(self, pts, cmp=abs):
1360                 # can be optimized
1361                 nearest = None
1362                 for cp in self.cut_list:
1363                         diff = cmp(cp[0] - pts)
1364                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1365                                 nearest = cp
1366                 return nearest
1367
1368         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1369                 current_pos = self.cueGetCurrentPosition()
1370                 if current_pos is None:
1371                         print "not seekable"
1372                         return
1373                 
1374                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1375                 
1376                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1377                         if onlyreturn:
1378                                 return nearest_cutpoint
1379                         if not onlyadd:
1380                                 self.removeMark(nearest_cutpoint)
1381                 elif not onlyremove and not onlyreturn:
1382                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1383                 
1384                 if onlyreturn:
1385                         return None
1386
1387         def addMark(self, point):
1388                 bisect.insort(self.cut_list, point)
1389                 self.uploadCuesheet()
1390
1391         def removeMark(self, point):
1392                 self.cut_list.remove(point)
1393                 self.uploadCuesheet()
1394
1395         def __getCuesheet(self):
1396                 service = self.session.nav.getCurrentService()
1397                 if service is None:
1398                         return None
1399                 return service.cueSheet()
1400
1401         def uploadCuesheet(self):
1402                 cue = self.__getCuesheet()
1403
1404                 if cue is None:
1405                         print "upload failed, no cuesheet interface"
1406                         return
1407                 cue.setCutList(self.cut_list)
1408
1409         def downloadCuesheet(self):
1410                 cue = self.__getCuesheet()
1411
1412                 if cue is None:
1413                         print "upload failed, no cuesheet interface"
1414                         return
1415                 self.cut_list = cue.getCutList()
1416
1417 class InfoBarSummary(Screen):
1418         skin = """
1419         <screen position="0,0" size="132,64">
1420                 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1421                 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1422         </screen>"""
1423
1424         def __init__(self, session, parent):
1425                 Screen.__init__(self, session)
1426                 self["CurrentService"] = ServiceName(self.session.nav)
1427                 self["Clock"] = Clock()
1428
1429 class InfoBarSummarySupport:
1430         def __init__(self):
1431                 pass
1432         
1433         def createSummary(self):
1434                 return InfoBarSummary
1435
1436 class InfoBarTeletextPlugin:
1437         def __init__(self):
1438                 self.teletext_plugin = None
1439                 
1440                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1441                         self.teletext_plugin = p
1442                 
1443                 if self.teletext_plugin is not None:
1444                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1445                                 {
1446                                         "startTeletext": (self.startTeletext, "View teletext...")
1447                                 })
1448                 else:
1449                         print "no teletext plugin found!"
1450
1451         def startTeletext(self):
1452                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())