add extension to swap picture in picture with big picture
[vuplus_dvbapp] / 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, configSelection, configElement_nonSave, getConfigListEntry
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         PIPSWAP = 3
1024
1025         def extensions(self):
1026                 list = []
1027                 if self.pipshown == False:
1028                         list.append((_("Activate Picture in Picture"), self.PIPON))
1029                 elif self.pipshown == True:
1030                         list.append((_("Disable Picture in Picture"), self.PIPOFF))
1031                         list.append((_("Move Picture in Picture"), self.MOVEPIP))
1032                         list.append((_("Swap services"), self.PIPSWAP))
1033                 self.session.openWithCallback(self.extensionCallback, ChoiceBox, title=_("Please choose an extension..."), list = list)
1034
1035         def extensionCallback(self, answer):
1036                 if answer is not None:
1037                         if answer[1] == self.PIPON:
1038                                 self.pip = self.session.instantiateDialog(PictureInPicture)
1039                                 
1040                                 newservice = self.session.nav.getCurrentlyPlayingServiceReference()
1041                                 
1042                                 if self.pip.playService(newservice):
1043                                         self.pipshown = True
1044                                 else:
1045                                         self.pipshown = False
1046                                         del self.pip
1047                                 self.session.nav.playService(newservice)
1048                         elif answer[1] == self.PIPOFF:
1049                                 del self.pip
1050                                 self.pipshown = False
1051                         elif answer[1] == self.PIPSWAP:
1052                                 swapservice = self.pip.getCurrentService()
1053                                 self.pip.playService(self.session.nav.getCurrentlyPlayingServiceReference())
1054                                 self.session.nav.playService(swapservice)
1055                                 
1056                         elif answer[1] == self.MOVEPIP:
1057                                 self.session.open(PiPSetup, pip = self.pip)
1058
1059 from RecordTimer import parseEvent
1060
1061 class InfoBarInstantRecord:
1062         """Instant Record - handles the instantRecord action in order to 
1063         start/stop instant records"""
1064         def __init__(self):
1065                 self["InstantRecordActions"] = HelpableActionMap(self, "InfobarInstantRecord",
1066                         {
1067                                 "instantRecord": (self.instantRecord, "Instant Record..."),
1068                         })
1069                 self.recording = []
1070                 self["BlinkingPoint"] = BlinkingPixmapConditional()
1071                 self["BlinkingPoint"].hide()
1072                 self["BlinkingPoint"].setConnect(self.session.nav.RecordTimer.isRecording)
1073
1074         def stopCurrentRecording(self, entry = -1):     
1075                 if entry is not None and entry != -1:
1076                         self.session.nav.RecordTimer.removeEntry(self.recording[entry])
1077                         self.recording.remove(self.recording[entry])
1078                         
1079         def startInstantRecording(self, limitEvent = False):
1080                 serviceref = self.session.nav.getCurrentlyPlayingServiceReference()
1081                 
1082                 # try to get event info
1083                 event = None
1084                 try:
1085                         service = self.session.nav.getCurrentService()
1086                         epg = eEPGCache.getInstance()
1087                         event = epg.lookupEventTime(serviceref, -1, 0)
1088                         if event is None:
1089                                 info = service.info()
1090                                 ev = info.getEvent(0)
1091                                 event = ev
1092                 except:
1093                         pass
1094
1095                 begin = time.time()
1096                 end = time.time() + 3600 * 10
1097                 name = "instant record"
1098                 description = ""
1099                 eventid = None
1100                 
1101                 if event is not None:
1102                         curEvent = parseEvent(event)
1103                         name = curEvent[2]
1104                         description = curEvent[3]
1105                         eventid = curEvent[4]
1106                         if limitEvent:
1107                                 end = curEvent[1]
1108                 else:
1109                         if limitEvent:
1110                                 self.session.open(MessageBox, _("No event info found, recording indefinitely."), MessageBox.TYPE_INFO)
1111                                 
1112                 data = (begin, end, name, description, eventid)
1113                 
1114                 recording = self.session.nav.recordWithTimer(serviceref, *data)
1115                 recording.dontSave = True
1116                 self.recording.append(recording)
1117                 
1118                 #self["BlinkingPoint"].setConnect(lambda: self.recording.isRunning())
1119                 
1120         def isInstantRecordRunning(self):
1121                 print "self.recording:", self.recording
1122                 if len(self.recording) > 0:
1123                         for x in self.recording:
1124                                 if x.isRunning():
1125                                         return True
1126                 return False
1127
1128         def recordQuestionCallback(self, answer):
1129                 print "pre:\n", self.recording
1130                 
1131                 if answer is None or answer[1] == "no":
1132                         return
1133                 list = []
1134                 recording = self.recording[:]
1135                 for x in recording:
1136                         if not x in self.session.nav.RecordTimer.timer_list:
1137                                 self.recording.remove(x)
1138                         elif x.dontSave and x.isRunning():
1139                                 list.append(TimerEntryComponent(x, False))              
1140
1141                 if answer[1] == "changeduration":
1142                         if len(self.recording) == 1:
1143                                 self.changeDuration(0)
1144                         else:
1145                                 self.session.openWithCallback(self.changeDuration, TimerSelection, list)
1146                 elif answer[1] == "stop":
1147                         if len(self.recording) == 1:
1148                                 self.stopCurrentRecording(0)
1149                         else:
1150                                 self.session.openWithCallback(self.stopCurrentRecording, TimerSelection, list)
1151                 if answer[1] == "indefinitely" or answer[1] == "manualduration" or answer[1] == "event":
1152                         limitEvent = False
1153                         if answer[1] == "event":
1154                                 limitEvent = True
1155                         if answer[1] == "manualduration":
1156                                 self.selectedEntry = len(self.recording)
1157                                 self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1158                         self.startInstantRecording(limitEvent = limitEvent)
1159                         
1160                 print "after:\n", self.recording
1161
1162         def changeDuration(self, entry):
1163                 if entry is not None:
1164                         self.selectedEntry = entry
1165                         self.session.openWithCallback(self.inputCallback, InputBox, title=_("How many minutes do you want to record?"), text="5", maxSize=False, type=Input.NUMBER)
1166
1167         def inputCallback(self, value):
1168                 if value is not None:
1169                         print "stopping recording after", int(value), "minutes."
1170                         self.recording[self.selectedEntry].end = time.time() + 60 * int(value)
1171                         self.session.nav.RecordTimer.timeChanged(self.recording[self.selectedEntry])
1172
1173         def instantRecord(self):
1174                 try:
1175                         stat = os.stat(resolveFilename(SCOPE_HDD))
1176                 except:
1177                         self.session.open(MessageBox, _("No HDD found or HDD not initialized!"), MessageBox.TYPE_ERROR)
1178                         return
1179         
1180                 if self.isInstantRecordRunning():
1181                         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")])
1182                 else:
1183                         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")])
1184
1185 from Tools.ISO639 import LanguageCodes
1186
1187 class InfoBarAudioSelection:
1188         def __init__(self):
1189                 self["AudioSelectionAction"] = HelpableActionMap(self, "InfobarAudioSelectionActions", 
1190                         {
1191                                 "audioSelection": (self.audioSelection, "Audio Options..."),
1192                         })
1193
1194         def audioSelection(self):
1195                 service = self.session.nav.getCurrentService()
1196                 audio = service.audioTracks()
1197                 self.audioTracks = audio
1198                 n = audio.getNumberOfTracks()
1199                 if n > 0:
1200 #                       self.audioChannel = service.audioChannel()
1201 #                       config.audio.audiochannel = configElement_nonSave("config.audio.audiochannel", configSelection, self.audioChannel.getCurrentChannel(), (("left", _("Left  >")), ("stereo", _("<  Stereo  >")), ("right", _("<  Right"))))
1202                         tlist = []
1203                         for x in range(n):
1204                                 i = audio.getTrackInfo(x)
1205                                 language = i.getLanguage()
1206                                 description = i.getDescription();
1207         
1208                                 if len(language) == 3:
1209                                         if language in LanguageCodes:
1210                                                 language = LanguageCodes[language][0]
1211         
1212                                 if len(description):
1213                                         description += " (" + language + ")"
1214                                 else:
1215                                         description = language
1216         
1217                                 tlist.append((description, x))
1218                         
1219                         selectedAudio = tlist[0][1]
1220                         tlist.sort(lambda x,y : cmp(x[0], y[0]))
1221
1222 #                       tlist.insert(0, getConfigListEntry(_("Audio Channel"), config.audio.audiochannel))
1223
1224                         selection = 0
1225                         for x in tlist:
1226                                 if x[1] != selectedAudio:
1227                                         selection += 1
1228                                 else:
1229                                         break
1230                         
1231                         self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = tlist, selection = selection)
1232                 else:
1233                         del self.audioTracks
1234
1235         def audioSelected(self, audio):
1236                 if audio is not None:
1237                         self.audioTracks.selectTrack(audio[1])
1238                 del self.audioTracks
1239 #               del self.audioChannel
1240 #               del config.audio.audiochannel
1241
1242 class InfoBarSubserviceSelection:
1243         def __init__(self):
1244                 self["SubserviceSelectionAction"] = HelpableActionMap(self, "InfobarSubserviceSelectionActions",
1245                         {
1246                                 "subserviceSelection": (self.subserviceSelection, _("Subservice list...")),
1247                         })
1248
1249                 self["SubserviceQuickzapAction"] = HelpableActionMap(self, "InfobarSubserviceQuickzapActions",
1250                         {
1251                                 "nextSubservice": (self.nextSubservice, _("Switch to next subservice")),
1252                                 "prevSubservice": (self.prevSubservice, _("Switch to previous subservice"))
1253                         }, -1)
1254                 self["SubserviceQuickzapAction"].setEnabled(False)
1255
1256                 self.session.nav.event.append(self.checkSubservicesAvail) # we like to get service events
1257
1258         def checkSubservicesAvail(self, ev):
1259                 if ev == iPlayableService.evUpdatedEventInfo:
1260                         service = self.session.nav.getCurrentService()
1261                         subservices = service.subServices()
1262                         if subservices.getNumberOfSubservices() == 0:
1263                                 self["SubserviceQuickzapAction"].setEnabled(False)
1264
1265         def nextSubservice(self):
1266                 self.changeSubservice(+1)
1267
1268         def prevSubservice(self):
1269                 self.changeSubservice(-1)
1270
1271         def changeSubservice(self, direction):
1272                 service = self.session.nav.getCurrentService()
1273                 subservices = service.subServices()
1274                 n = subservices.getNumberOfSubservices()
1275                 if n > 0:
1276                         selection = -1
1277                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1278                         for x in range(n):
1279                                 if subservices.getSubservice(x).toString() == ref.toString():
1280                                         selection = x
1281                         if selection != -1:
1282                                 selection += direction
1283                                 if selection >= n:
1284                                         selection=0
1285                                 elif selection < 0:
1286                                         selection=n-1
1287                                 newservice = subservices.getSubservice(selection)
1288                                 if newservice.valid():
1289                                         del subservices
1290                                         del service
1291                                         self.session.nav.playService(newservice)
1292
1293         def subserviceSelection(self):
1294                 service = self.session.nav.getCurrentService()
1295                 subservices = service.subServices()
1296                 
1297                 n = subservices.getNumberOfSubservices()
1298                 selection = 0
1299                 if n > 0:
1300                         ref = self.session.nav.getCurrentlyPlayingServiceReference()
1301                         tlist = []
1302                         for x in range(n):
1303                                 i = subservices.getSubservice(x)
1304                                 if i.toString() == ref.toString():
1305                                         selection = x
1306                                 tlist.append((i.getName(), i))
1307
1308                         self.session.openWithCallback(self.subserviceSelected, ChoiceBox, title=_("Please select a subservice..."), list = tlist, selection = selection)
1309
1310         def subserviceSelected(self, service):
1311                 if not service is None:
1312                         self["SubserviceQuickzapAction"].setEnabled(True)
1313                         self.session.nav.playService(service[1])
1314
1315 class InfoBarAdditionalInfo:
1316         def __init__(self):
1317                 self["DolbyActive"] = Pixmap()
1318                 self["CryptActive"] = Pixmap()
1319                 self["FormatActive"] = Pixmap()
1320                 
1321                 self["ButtonRed"] = PixmapConditional(withTimer = False)
1322                 self["ButtonRed"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1323                 self.onLayoutFinish.append(self["ButtonRed"].update)
1324                 self["ButtonRedText"] = LabelConditional(text = _("Record"), withTimer = False)
1325                 self["ButtonRedText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1326                 self.onLayoutFinish.append(self["ButtonRedText"].update)
1327
1328                 self["ButtonGreen"] = Pixmap()
1329                 self["ButtonGreenText"] = Label(_("Subservices"))
1330
1331                 self["ButtonYellow"] = PixmapConditional(withTimer = False)
1332                 self["ButtonYellow"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1333                 self["ButtonYellowText"] = LabelConditional(text = _("Timeshifting"), withTimer = False)
1334                 self["ButtonYellowText"].setConnect(lambda: harddiskmanager.HDDCount() > 0)
1335                 self.onLayoutFinish.append(self["ButtonYellow"].update)
1336                 self.onLayoutFinish.append(self["ButtonYellowText"].update)
1337
1338                 self["ButtonBlue"] = PixmapConditional(withTimer = False)
1339                 self["ButtonBlue"].setConnect(lambda: True)
1340                 self["ButtonBlueText"] = LabelConditional(text = _("Extensions"), withTimer = False)
1341                 self["ButtonBlueText"].setConnect(lambda: True)
1342                 self.onLayoutFinish.append(self["ButtonBlue"].update)
1343                 self.onLayoutFinish.append(self["ButtonBlueText"].update)
1344
1345                 self.session.nav.event.append(self.gotServiceEvent) # we like to get service events
1346
1347         def hideSubServiceIndication(self):
1348                 self["ButtonGreen"].hide()
1349                 self["ButtonGreenText"].hide()
1350
1351         def showSubServiceIndication(self):
1352                 self["ButtonGreen"].show()
1353                 self["ButtonGreenText"].show()
1354
1355         def checkFormat(self, service):
1356                 info = service.info()
1357                 if info is not None:
1358                         aspect = info.getInfo(iServiceInformation.sAspect)
1359                         if aspect in [ 3, 4, 7, 8, 0xB, 0xC, 0xF, 0x10 ]:
1360                                 self["FormatActive"].show()
1361                         else:
1362                                 self["FormatActive"].hide()
1363
1364         def checkSubservices(self, service):
1365                 if service.subServices().getNumberOfSubservices() > 0:
1366                         self.showSubServiceIndication()
1367                 else:
1368                         self.hideSubServiceIndication()
1369
1370         def checkDolby(self, service):
1371                 # FIXME
1372                 dolby = False
1373                 audio = service.audioTracks()
1374                 if audio is not None:
1375                         n = audio.getNumberOfTracks()
1376                         for x in range(n):
1377                                 i = audio.getTrackInfo(x)
1378                                 description = i.getDescription();
1379                                 if description.find("AC3") != -1 or description.find("DTS") != -1:
1380                                         dolby = True
1381                                         break
1382                 if dolby:
1383                         self["DolbyActive"].show()
1384                 else:
1385                         self["DolbyActive"].hide()
1386
1387         def checkCrypted(self, service):
1388                 info = service.info()
1389                 if info is not None:
1390                         if info.getInfo(iServiceInformation.sIsCrypted) > 0:
1391                                 self["CryptActive"].show()
1392                         else:
1393                                 self["CryptActive"].hide()
1394
1395         def gotServiceEvent(self, ev):
1396                 service = self.session.nav.getCurrentService()
1397                 if ev == iPlayableService.evUpdatedEventInfo:
1398                         self.checkSubservices(service)
1399                         self.checkFormat(service)
1400                 elif ev == iPlayableService.evUpdatedInfo:
1401                         self.checkCrypted(service)
1402                         self.checkDolby(service)
1403                 elif ev == iPlayableService.evEnd:
1404                         self.hideSubServiceIndication()
1405                         self["CryptActive"].hide()
1406                         self["DolbyActive"].hide()
1407                         self["FormatActive"].hide()
1408
1409 class InfoBarNotifications:
1410         def __init__(self):
1411                 self.onExecBegin.append(self.checkNotifications)
1412                 Notifications.notificationAdded.append(self.checkNotificationsIfExecing)
1413         
1414         def checkNotificationsIfExecing(self):
1415                 if self.execing:
1416                         self.checkNotifications()
1417
1418         def checkNotifications(self):
1419                 if len(Notifications.notifications):
1420                         n = Notifications.notifications[0]
1421                         Notifications.notifications = Notifications.notifications[1:]
1422                         print "open",n
1423                         cb = n[0]
1424                         if cb is not None:
1425                                 self.session.openWithCallback(cb, n[1], *n[2], **n[3])
1426                         else:
1427                                 self.session.open(n[1], *n[2], **n[3])
1428
1429 class InfoBarServiceNotifications:
1430         def __init__(self):
1431                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1432                         {
1433                                 iPlayableService.evEnd: self.serviceHasEnded
1434                         })
1435
1436         def serviceHasEnded(self):
1437                 print "service end!"
1438
1439                 try:
1440                         self.setSeekState(self.SEEK_STATE_PLAY)
1441                 except:
1442                         pass
1443
1444 class InfoBarCueSheetSupport:
1445         CUT_TYPE_IN = 0
1446         CUT_TYPE_OUT = 1
1447         CUT_TYPE_MARK = 2
1448         
1449         def __init__(self):
1450                 self["CueSheetActions"] = HelpableActionMap(self, "InfobarCueSheetActions", 
1451                         {
1452                                 "jumpPreviousMark": (self.jumpPreviousMark, "jump to next marked position"),
1453                                 "jumpNextMark": (self.jumpNextMark, "jump to previous marked position"),
1454                                 "toggleMark": (self.toggleMark, "toggle a cut mark at the current position")
1455                         }, prio=1) 
1456                 
1457                 self.cut_list = [ ]
1458                 self.__event_tracker = ServiceEventTracker(screen=self, eventmap=
1459                         {
1460                                 iPlayableService.evStart: self.__serviceStarted,
1461                         })
1462
1463         def __serviceStarted(self):
1464                 print "new service started! trying to download cuts!"
1465                 self.downloadCuesheet()
1466
1467         def __getSeekable(self):
1468                 service = self.session.nav.getCurrentService()
1469                 if service is None:
1470                         return None
1471                 return service.seek()
1472
1473         def cueGetCurrentPosition(self):
1474                 seek = self.__getSeekable()
1475                 if seek is None:
1476                         return None
1477                 r = seek.getPlayPosition()
1478                 if r[0]:
1479                         return None
1480                 return long(r[1])
1481
1482         def jumpPreviousNextMark(self, cmp, alternative=None):
1483                 current_pos = self.cueGetCurrentPosition()
1484                 if current_pos is None:
1485                         return
1486                 mark = self.getNearestCutPoint(current_pos, cmp=cmp)
1487                 if mark is not None:
1488                         pts = mark[0]
1489                 elif alternative is not None:
1490                         pts = alternative
1491                 else:
1492                         return
1493
1494                 seekable = self.__getSeekable()
1495                 if seekable is not None:
1496                         seekable.seekTo(pts)
1497
1498         def jumpPreviousMark(self):
1499                 # we add 2 seconds, so if the play position is <2s after
1500                 # the mark, the mark before will be used
1501                 self.jumpPreviousNextMark(lambda x: -x-5*90000, alternative=0)
1502
1503         def jumpNextMark(self):
1504                 self.jumpPreviousNextMark(lambda x: x)
1505
1506         def getNearestCutPoint(self, pts, cmp=abs):
1507                 # can be optimized
1508                 nearest = None
1509                 for cp in self.cut_list:
1510                         diff = cmp(cp[0] - pts)
1511                         if diff >= 0 and (nearest is None or cmp(nearest[0] - pts) > diff):
1512                                 nearest = cp
1513                 return nearest
1514
1515         def toggleMark(self, onlyremove=False, onlyadd=False, tolerance=5*90000, onlyreturn=False):
1516                 current_pos = self.cueGetCurrentPosition()
1517                 if current_pos is None:
1518                         print "not seekable"
1519                         return
1520                 
1521                 nearest_cutpoint = self.getNearestCutPoint(current_pos)
1522                 
1523                 if nearest_cutpoint is not None and abs(nearest_cutpoint[0] - current_pos) < tolerance:
1524                         if onlyreturn:
1525                                 return nearest_cutpoint
1526                         if not onlyadd:
1527                                 self.removeMark(nearest_cutpoint)
1528                 elif not onlyremove and not onlyreturn:
1529                         self.addMark((current_pos, self.CUT_TYPE_MARK))
1530                 
1531                 if onlyreturn:
1532                         return None
1533
1534         def addMark(self, point):
1535                 bisect.insort(self.cut_list, point)
1536                 self.uploadCuesheet()
1537
1538         def removeMark(self, point):
1539                 self.cut_list.remove(point)
1540                 self.uploadCuesheet()
1541
1542         def __getCuesheet(self):
1543                 service = self.session.nav.getCurrentService()
1544                 if service is None:
1545                         return None
1546                 return service.cueSheet()
1547
1548         def uploadCuesheet(self):
1549                 cue = self.__getCuesheet()
1550
1551                 if cue is None:
1552                         print "upload failed, no cuesheet interface"
1553                         return
1554                 cue.setCutList(self.cut_list)
1555
1556         def downloadCuesheet(self):
1557                 cue = self.__getCuesheet()
1558
1559                 if cue is None:
1560                         print "upload failed, no cuesheet interface"
1561                         return
1562                 self.cut_list = cue.getCutList()
1563
1564 class InfoBarSummary(Screen):
1565         skin = """
1566         <screen position="0,0" size="132,64">
1567                 <widget name="Clock" position="50,46" size="82,18" font="Regular;16" />
1568                 <widget name="CurrentService" position="0,4" size="132,42" font="Regular;18" />
1569         </screen>"""
1570
1571         def __init__(self, session, parent):
1572                 Screen.__init__(self, session)
1573                 self["CurrentService"] = ServiceName(self.session.nav)
1574                 self["Clock"] = Clock()
1575
1576 class InfoBarSummarySupport:
1577         def __init__(self):
1578                 pass
1579         
1580         def createSummary(self):
1581                 return InfoBarSummary
1582
1583 class InfoBarTeletextPlugin:
1584         def __init__(self):
1585                 self.teletext_plugin = None
1586                 
1587                 for p in plugins.getPlugins(PluginDescriptor.WHERE_TELETEXT):
1588                         self.teletext_plugin = p
1589                 
1590                 if self.teletext_plugin is not None:
1591                         self["TeletextActions"] = HelpableActionMap(self, "InfobarTeletextActions",
1592                                 {
1593                                         "startTeletext": (self.startTeletext, "View teletext...")
1594                                 })
1595                 else:
1596                         print "no teletext plugin found!"
1597
1598         def startTeletext(self):
1599                 self.teletext_plugin(session=self.session, service=self.session.nav.getCurrentService())