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